import os
import shutil
import pytest
import logging
from calculate.variables.datavars import NamespaceNode, VariableNode,\
                                         Namespace, Variable, Dependence,\
                                         CyclicVariableError, HashType,\
                                         StringType, IntegerType,\
                                         VariableType, VariableError,\
                                         TableType, BooleanType,\
                                         VariableTypeError, FloatType,\
                                         ListType, VariableNotFoundError,\
                                         Copy, Format, Calculate, Static
from calculate.templates.template_engine import TemplateEngine, FILE
from calculate.templates.template_processor import TemplateExecutor
from calculate.variables.loader import NamespaceIniFiller, Datavars
from calculate.utils.files import stderr_devnull, read_file


TESTFILES_PATH = os.path.join(os.getcwd(), 'tests/variables/testfiles')

APPENDS_SET = TemplateExecutor(cl_config_path=os.path.join(
                                          TESTFILES_PATH,
                                          'var/lib/calculate/config')
                               ).available_appends


class LoggingHandler(logging.Handler):
    '''Тестовый хэндлер для логгирования в список, который затем можно
    проверять в тестах.'''
    def __init__(self, msg_list: list):
        logging.Handler.__init__(self)
        # Список для хранения сообщений логгера.
        self.msg_list = msg_list

    def emit(self, record):
        log_record = (record.levelname, record.getMessage())
        self.msg_list.append(log_record)


@pytest.mark.vars
class TestDatavars:
    # Сначала тестируем классы и методы необходимые для построения дерева
    # переменных и пространств имен.
    def test_if_NamespaceNode_just_initialized_with_its_name__the_NamespaceNode_object_contains_empty_namespaces_and_variables_dictionaries_and_fullname_method_returns_only_the_namespace_s_name(self):
        namespace_1 = NamespaceNode('namespace_1')

        assert namespace_1.namespaces == dict()
        assert namespace_1.variables == dict()
        assert namespace_1.get_fullname() == 'namespace_1'

    def test_if_namespace_node_added_in_an_other_namespace_node__a_parent_namespace_node_contains_child_namespace_in_its_namespaces_dictionary_and_child_namespace_object_has_parent_namespace_in_its_parent_attribute_and_its_fullname_method_returns_names_of_both_namespaces(self):
        namespace_1 = NamespaceNode('namespace_1')
        namespace_2 = NamespaceNode('namespace_2')
        namespace_1.add_namespace(namespace_2)

        assert namespace_1.namespaces == {'namespace_2': namespace_2}
        assert namespace_1.variables == dict()
        assert namespace_1.get_fullname() == 'namespace_1'

        assert namespace_1.namespace_2 == namespace_2
        assert namespace_1['namespace_2'] == namespace_2

        assert namespace_2.namespaces == dict()
        assert namespace_2.variables == dict()
        assert namespace_2.parent == namespace_1
        assert namespace_2.get_fullname() == 'namespace_1.namespace_2'

    def test_if_two_VariableNode_objects_are_initialized_and_added_to_a_namespace__the_NamespaceNode_object_contains_this_variables_and_can_be_used_to_get_variables_values_and_variables_have_namespace_name_in_their_fullnames(self):
        namespace_1 = NamespaceNode('namespace_1')
        variable_1 = VariableNode('var_1', namespace_1, source='value_1')
        variable_2 = VariableNode('var_2', namespace_1, source='value_2')

        assert namespace_1.namespaces == dict()
        assert namespace_1.variables == {'var_1': variable_1,
                                         'var_2': variable_2}
        assert namespace_1.get_fullname() == 'namespace_1'

        assert namespace_1.var_1 == 'value_1'
        assert namespace_1['var_1'] == variable_1

        assert namespace_1.var_2 == 'value_2'
        assert namespace_1['var_2'] == variable_2

        assert namespace_1['var_1'].get_fullname() == 'namespace_1.var_1'
        assert namespace_1['var_2'].get_fullname() == 'namespace_1.var_2'

    def test_if_two_dependencies_is_created_with_the_same_variables_and_their_depend_functions_are_equivalent_lambdas__the_dependencies_are_equal(self):
        namespace_1 = NamespaceNode('namespace_1')
        variable_1 = VariableNode('var_1', namespace_1, source=2,
                                  variable_type=IntegerType)
        variable_2 = VariableNode('var_2', namespace_1, source=4,
                                  variable_type=IntegerType)

        dependence_1 = Dependence(
                            variable_1, variable_2, depend=lambda arg_1, arg_2:
                            'greater' if arg_1.value > arg_2.value else 'less')

        dependence_2 = Dependence(
                            variable_1, variable_2, depend=lambda var_1, var_2:
                            'greater' if var_1.value > var_2.value else 'less')
        assert dependence_1 == dependence_2

    def test_if_two_dependencies_is_created_with_the_same_variables_and_their_depend_functions_are_equivalent_lambda_and_function__the_dependencies_are_equal(self):
        namespace_1 = NamespaceNode('namespace_1')
        variable_1 = VariableNode('var_1', namespace_1, source=2,
                                  variable_type=IntegerType)
        variable_2 = VariableNode('var_2', namespace_1, source=4,
                                  variable_type=IntegerType)

        dependence_1 = Dependence(
                            variable_1, variable_2, depend=lambda arg_1, arg_2:
                            'greater' if arg_1.value > arg_2.value else 'less')

        def comparator(var_1, var_2):
            if var_1.value > var_2.value:
                return 'greater'
            else:
                return 'less'
        dependence_2 = Dependence(variable_1, variable_2, depend=comparator)
        assert dependence_1 == dependence_2

    def test_if_two_dependencies_is_created_with_the_same_variables_and_their_depend_functions_are_not_equivalent_lambdas__the_dependencies_are_equal(self):
        namespace_1 = NamespaceNode('namespace_1')
        variable_1 = VariableNode('var_1', namespace_1, source=2,
                                  variable_type=IntegerType)
        variable_2 = VariableNode('var_2', namespace_1, source=4,
                                  variable_type=IntegerType)

        dependence_1 = Dependence(
                            variable_1, variable_2, depend=lambda arg_1, arg_2:
                            'less' if arg_1.value > arg_2.value else 'greater')

        dependence_2 = Dependence(
                            variable_1, variable_2, depend=lambda var_1, var_2:
                            'greater' if var_1.value > var_2.value else 'less')
        assert dependence_1 != dependence_2

    def test_if_two_dependencies_is_created_with_the_same_variables_and_their_depend_functions_are_not_equivalent_lambda_and_function__the_dependencies_are_equal(self):
        namespace_1 = NamespaceNode('namespace_1')
        variable_1 = VariableNode('var_1', namespace_1, source=2,
                                  variable_type=IntegerType)
        variable_2 = VariableNode('var_2', namespace_1, source=4,
                                  variable_type=IntegerType)

        dependence_1 = Dependence(
                            variable_1, variable_2, depend=lambda arg_1, arg_2:
                            'greater' if arg_1.value > arg_2.value else 'less')

        def comparator(var_1, var_2):
            if var_1 > var_2:
                return 'less'
            else:
                return 'greater'
        dependence_2 = Dependence(variable_1, variable_2, depend=comparator)
        assert dependence_1 != dependence_2

    def test_if_a_variable_subscribed_to_two_other_variables_using_set_function__the_variable_updates_using_the_variables_and_the_set_function_to_calculate_its_value(self):
        namespace_1 = NamespaceNode('namespace_1')
        variable_1 = VariableNode('var_1', namespace_1, source=2,
                                  variable_type=IntegerType)
        variable_2 = VariableNode('var_2', namespace_1, source=4,
                                  variable_type=IntegerType)

        namespace_2 = NamespaceNode('namespace_2')
        variable = VariableNode('var_1', namespace_2)
        variable.source = Dependence(
                            variable_1, variable_2, depend=lambda var_1, var_2:
                            'greater' if var_1.value > var_2.value else 'less')

        assert namespace_2.var_1 == 'less'

        namespace_1['var_1'].source = 5
        assert namespace_2.var_1 == 'greater'

    # Теперь тестируем интерфейс создания переменных.
    def test_if_variables_creation_api_is_used_for_creating_of_a_number_of_variables_without_any_dependencies_in_their_sources__the_api_fabrics_creates_the_variables(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='val_1')

            Variable('var_2', source=2, type=IntegerType)

            Variable('var_3', source=4, type=IntegerType)

            with Namespace('namespace_1_1'):
                Variable('var_1', source='val_1')

        assert datavars.namespace_1.var_1 == 'val_1'
        assert datavars.namespace_1.var_2 == 2
        assert datavars.namespace_1.var_3 == 4
        assert datavars.namespace_1.namespace_1_1.var_1 == 'val_1'

    def test_if_variables_creation_api_is_used_for_creating_of_a_namespace_and_of_a_number_of_variables_and_there_is_attempt_to_get_value_of_an_unexistent_variable_from_a_created_namespace__the_namespace_raises_the_VariableError_exception(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='val_1')
            Variable('var_2', source=2, type=IntegerType)
            Variable('var_3', source=4, type=IntegerType)

        with pytest.raises(VariableError):
            datavars.namespace_1.var_4

    def test_if_dependence_interface_object_is_used_for_finding_an_existing_variable_from_an_other_namespace_using_absolute_variable_name__the_find_variable_method_returns_the_desired_variable(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='val_1', type=StringType)

        with Namespace('namespace_2'):
            assert Dependence._get_variable('namespace_1.var_1') ==\
                datavars.namespace_1['var_1']

    def test_if_dependence_interface_object_is_used_for_finding_an_unexisting_variable_from_an_other_namespace_using_absolute_variable_name__the_find_variable_method_raises_the_VariableError_exception(self):
        Namespace.reset()

        with Namespace('namespace_1'):
            Variable('var_1', source='val_1', type=StringType)

        with Namespace('namespace_2'):
            with pytest.raises(VariableError):
                Dependence._get_variable('namespace_1.var_2')

    def test_if_dependence_interface_object_is_used_for_finding_an_existing_variable_from_a_same_namespace_using_relative_variable_name__the_find_variable_method_returns_the_desired_variable(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='val_1', type=StringType)
            Variable('var_2', source=2, type=IntegerType)
            Variable('var_3', source=4, type=IntegerType)

            with Namespace('namespace_1_1'):
                Variable('var_1', source='val_1', type=StringType)

                assert Dependence._get_variable('.var_1') ==\
                    datavars.namespace_1.namespace_1_1['var_1']
                assert Dependence._get_variable('..var_3') ==\
                    datavars.namespace_1['var_3']

    def test_if_dependence_interface_object_is_used_for_finding_an_unexisting_variable_from_a_same_namespace_using_relative_variable_name__the_find_variable_method_raises_the_VariableError_exception(self):
        Namespace.reset()

        with Namespace('namespace_1'):
            Variable('var_1', source='val_1', type=StringType)
            Variable('var_2', source=2, type=IntegerType)

            with Namespace('namespace_1_1'):
                with pytest.raises(VariableError):
                    Dependence._get_variable('..var_3')

    def test_if_variable_is_created_with_dependence_in_its_source_and_the_dependence_has_only_set_function_without_any_arguments__the_variable_updates_its_value_using_only_set_function(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            def source_function():
                return 'value_from_function'
            Variable('var', source=Dependence(depend=source_function),
                     type=VariableType)

        assert datavars.namespace_1.var == 'value_from_function'

    def test_if_a_variable_is_created_using_a_Copy_of_a_variable_in_the_source__the_new_variable_uses_this_variable_s_value_to_create_its_own_value(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='value_1', type=StringType)
            Variable('var_2', type=StringType, source=Copy('.var_1'))

        assert datavars.namespace_1.var_2 == 'value_1'

    def test_if_a_variable_is_created_using_a_Format_with_a_format_string_and_a_variable_in_the_source__the_new_variable_uses_the_variable_and_format_string_to_produce_a_new_string_value(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='value_1', type=StringType)
            Variable('var_2', type=StringType,
                     source=Format('var_1 = { .var_1 }'))

        assert datavars.namespace_1.var_2 == 'var_1 = value_1'

    def test_if_a_variable_is_created_using_a_Calculate_with_a_variable_and_some_function_in_the_source__the_new_variable_uses_the_variable_and_the_function_for_calculating_of_its_value(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='value_1', type=StringType)

            def formatter(var_1):
                return 'var_1 = {}'.format(var_1.value)
            Variable('var_2', type=StringType,
                     source=Calculate(formatter, '.var_1'))

        assert datavars.namespace_1.var_2 == 'var_1 = value_1'

    def test_if_a_variable_is_created_using_a_Dependence_with_a_static_value_a_variable_and_depend_function_in_the_source__the_new_variable_uses_the_variable_the_function_and_the_static_value_for_calculating_of_its_value(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            def source_function(static_value):
                return 'static value = {}'.format(static_value.value)
            Variable('var', type=StringType,
                     source=Dependence(Static('value'),
                                       depend=source_function))

        assert datavars.namespace_1.var == 'static value = value'

    def test_if_a_variable_is_created_using_a_Calculate_with_a_static_value_a_variable_and_some_function_in_the_source__the_new_variable_uses_the_variable_the_function_and_the_static_value_for_calculating_of_its_value(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='var value', type=StringType)

            def suspicious_function(static_value, var_1):
                return '{} and {}'.format(static_value.value, var_1.value)
            Variable('var_2', type=StringType,
                     source=Calculate(suspicious_function,
                                      Static('static value'), '.var_1'))

        assert datavars.namespace_1.var_2 == 'static value and var value'

    def test_if_a_variable_is_created_using_a_Calculate_with_a_non_string_value_a_variable_and_some_function_in_the_source__the_new_variable_uses_the_variable_the_function_and_the_static_value_for_calculating_of_its_value(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source=10, type=IntegerType)

            def suspicious_function(static_value, var_1):
                return static_value.value + var_1.value
            Variable('var_2', type=IntegerType,
                     source=Calculate(suspicious_function,
                                      12, '.var_1'))

        assert datavars.namespace_1.var_2 == 22

    def test_if_a_variable_is_created_using_dependence_with_three_variables_and_a_set_function_but_during_the_calculation_only_two_variables_was_used__the_unused_variable_is_not_in_variable_subscriptions_and_is_invalidated_before_first_usage(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source=5, type=IntegerType)
            Variable('var_2', source='less')
            Variable('var_3', source='greater')

            def solver(arg_1, arg_2, arg_3):
                if arg_1.value < 6:
                    return arg_2.value
                else:
                    return arg_3.value
            Variable('var_4', type=StringType,
                     source=Dependence('.var_1', '.var_2', '.var_3',
                                       depend=solver))

        assert datavars.namespace_1.var_4 == datavars.namespace_1.var_2
        assert datavars.namespace_1['var_1'] in\
            datavars.namespace_1['var_4']._subscriptions
        assert datavars.namespace_1['var_2'] in\
            datavars.namespace_1['var_4']._subscriptions
        assert datavars.namespace_1['var_3'] not in\
            datavars.namespace_1['var_4']._subscriptions
        assert datavars.namespace_1['var_3'].value is None

    def test_if_variables_creation_api_is_used_for_creating_of_a_number_of_variables_with_existing_variables_dependencies_in_their_sources__the_api_fabrics_creates_the_variables(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            var_1 = Variable('var_1', source=2, type=IntegerType)
            Variable('var_2', source=4, type=IntegerType)

        with Namespace('namespace_2'):
            Variable('var_1', source=Dependence(
                      var_1, '..namespace_1.var_2', depend=lambda arg_1, arg_2:
                      'greater' if arg_1.value > arg_2.value else 'less'))

        assert datavars.namespace_1.var_1 == 2
        assert datavars.namespace_1.var_2 == 4
        assert datavars.namespace_2.var_1 == 'less'

    def test_if_variable_has_variable_dependence_in_its_source_and_the_subscription_variable_source_is_changed__the_subscriber_is_invalidated_and_then_updates_its_value(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            var_1 = Variable('var_1', source=2, type=IntegerType)
            Variable('var_2', source=4, type=IntegerType)

        with Namespace('namespace_2'):
            Variable('var_1', source=Dependence(
                      var_1, '..namespace_1.var_2', depend=lambda arg_1, arg_2:
                      'greater' if arg_1.value > arg_2.value else 'less'))

        datavars = Namespace.datavars
        assert datavars.namespace_2.var_1 == 'less'

        datavars.namespace_1['var_1'].source = 5
        assert datavars.namespace_2['var_1']._invalidated
        assert datavars.namespace_2.var_1 == 'greater'

    def test_if_variable_was_created_using_variables_api_and_then_was_created_again_with_simple_source_value__the_variable_just_changes_its_source(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            var_1 = Variable('var_1', source='value_1')
            Variable('var_2', source=Dependence(
                                         var_1, depend=lambda arg_1:
                                         'from var_1: {}'.format(arg_1.value)))
        assert datavars.namespace_1.var_1 == 'value_1'
        assert datavars.namespace_1.var_2 == 'from var_1: value_1'

        with Namespace('namespace_1'):
            Variable('var_1', source='value_2')

        assert datavars.namespace_1.var_1 == 'value_2'
        assert datavars.namespace_1.var_2 == 'from var_1: value_2'

    def test_if_variable_was_created_using_variables_api_and_then_was_created_again_with_a_new_dependency_as_its_source__the_variable_just_changes_its_source_to_the_new_dependency(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='value_1')
            Variable('var_2', source=Dependence(
                                         '.var_1', depend=lambda arg_1:
                                         'from var_1: {}'.format(arg_1.value)))
            Variable('var_3', source='value_3')

        assert datavars.namespace_1.var_1 == 'value_1'
        assert datavars.namespace_1.var_2 == 'from var_1: value_1'

        with Namespace('namespace_1'):
            Variable('var_2', source=Dependence(
                                         '.var_3', depend=lambda arg_1:
                                         'from var_3: {}'.format(arg_1.value)))

        assert datavars.namespace_1.var_2 == 'from var_3: value_3'

    def test_if_some_variables_created_with_dependencies_in_its_sources_and_subscribed_to_each_other__variables_raise_the_CyclicVariableError_exception_while_its_calculating(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='value_1')
            Variable('var_2', source=Dependence(
                                      'namespace_1.var_1', depend=lambda arg_1:
                                      'from var_1: {}'.format(arg_1.value)))

            def comparator(arg_1):
                return 'from var_2: {}'.format(arg_1.value)
            Variable('var_3', source=Dependence('.var_2', depend=comparator))
            Variable('var_1',
                     source=Dependence('.var_3', depend=lambda arg_1:
                                       'from var_3: {}'.format(arg_1.value)))

        with pytest.raises(CyclicVariableError):
            datavars.namespace_1.var_3

    def test_if_variables_with_string_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_strings(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='value', type=StringType)
            Variable('var_2', source=2, type=StringType)
            Variable('var_3', source=False, type=StringType)

        assert datavars.namespace_1.var_1 == 'value'
        assert datavars.namespace_1.var_2 == '2'
        assert datavars.namespace_1.var_3 == 'False'

    def test_if_variables_with_integer_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_integers(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source=1, type=IntegerType)
            Variable('var_2', source='2', type=IntegerType)
            Variable('var_3', source=3.1, type=IntegerType)
            Variable('var_4', source='-4', type=IntegerType)

        assert datavars.namespace_1.var_1 == 1
        assert datavars.namespace_1.var_2 == 2
        assert datavars.namespace_1.var_3 == 3
        assert datavars.namespace_1.var_4 == -4

    def test_if_variable_with_integer_type_is_created_with_source_that_can_not_be_converted_to_an_integer_value__the_variable_api_raises_the_VariableTypeError_exception(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='a', type=IntegerType)

        with pytest.raises(VariableTypeError):
            datavars.namespace_1.var_1

    def test_if_variables_with_float_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_floats(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source=1.1, type=FloatType)
            Variable('var_2', source='2.2', type=FloatType)
            Variable('var_3', source=3, type=FloatType)
            Variable('var_4', source='-4.4', type=FloatType)

        assert datavars.namespace_1.var_1 == 1.1
        assert datavars.namespace_1.var_2 == 2.2
        assert datavars.namespace_1.var_3 == 3.0
        assert datavars.namespace_1.var_4 == -4.4

    def test_if_variable_with_float_type_is_created_with_source_that_can_not_be_converted_to_an_float_value__the_variable_api_raises_the_VariableTypeError_exception(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='a', type=FloatType)

        with pytest.raises(VariableTypeError):
            datavars.namespace_1.var_1

    def test_if_variables_with_bool_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_bool(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source=True, type=BooleanType)
            Variable('var_2', source='false', type=BooleanType)
            Variable('var_3', source='True', type=BooleanType)
            Variable('var_4', source='0', type=BooleanType)

        assert datavars.namespace_1.var_1 is True
        assert datavars.namespace_1.var_2 is False
        assert datavars.namespace_1.var_3 is True
        assert datavars.namespace_1.var_4 is True

    def test_if_variables_with_list_type_is_created_with_correct_source__the_variable_api_creates_this_variables_and_the_variables_contains_lists(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source=['a', 'b', 'c'], type=ListType)
            Variable('var_2', source='a, b, c', type=ListType)
            Variable('var_3', source={'a', 'b', 'c'}, type=ListType)

        assert datavars.namespace_1.var_1 == ['a', 'b', 'c']
        assert datavars.namespace_1.var_2 == ['a', 'b', 'c']
        assert datavars.namespace_1.var_3 == list({'a', 'b', 'c'})

    def test_if_variable_with_list_type_is_created_with_source_that_can_not_be_converted_to_an_list_value__the_variable_api_raises_the_VariableTypeError_exception(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source=1, type=ListType)

        with pytest.raises(VariableTypeError):
            datavars.namespace_1.var_1

    def test_if_hash_variable_is_created_with_Hash_type_and_with_a_dictionary_in_its_source__the_variable_is_created_with_Hash_value_in_it(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', type=HashType, source={'key_1': 'value_1',
                                                     'key_2': 'value_2'})

        assert datavars.namespace_1.var_1.key_1 == 'value_1'
        assert datavars.namespace_1.var_1.key_2 == 'value_2'

        with Namespace('namespace_1'):
            Variable('var_1', source={'key_1': 'another_value',
                                      'key_2': 'other_value'})

        assert datavars.namespace_1.var_1.key_1 == 'another_value'
        assert datavars.namespace_1.var_1.key_2 == 'other_value'

    def test_if_hash_variable_is_created_with_Hash_type_and_with_a_dependence_in_its_source__the_variable_is_created_with_Hash_value_in_it_using_dictionary_returned_by_depend_function(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='value_1', type=StringType)
            Variable('var_2', type=HashType, source={'key_1': 'value_1',
                                                     'key_2': 'value_2'})
            Variable('var_3', source='value_3', type=StringType)

            def depend_hash(arg_1, arg_2):
                return {'key_1': 'value',
                        'key_2': arg_1.value,
                        'key_3': arg_2.value}
            Variable('var_4', type=HashType,
                     source=Dependence('.var_1', '.var_2.key_1',
                                       depend=depend_hash))

        assert datavars.namespace_1.var_4.key_1 == 'value'
        assert datavars.namespace_1.var_4.key_2 == 'value_1'
        assert datavars.namespace_1.var_4.key_3 == 'value_1'

        with Namespace('namespace_1'):
            Variable('var_1', source='other_value', type=StringType)
            Variable('var_2', source={'key_1': 'another_value',
                                      'key_2': 'value_2'})

        assert datavars.namespace_1.var_2.key_1 == 'another_value'
        assert datavars.namespace_1.var_4.key_2 == 'other_value'
        assert datavars.namespace_1.var_4.key_3 == 'another_value'

    def test_if_hash_variable_is_created_as_not_fixed_and_then_changed_using_dictionary_with_a_new_key__the_hash_adds_a_new_key_to_the_first_hash(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', type=HashType, source={'key_1': 'value_1',
                                                     'key_2': 'value_2'})

        assert datavars.namespace_1.var_1.key_1 == 'value_1'
        assert datavars.namespace_1.var_1.key_2 == 'value_2'

        with Namespace('namespace_1'):
            Variable('var_1', source={'key_1': 'another_value',
                                      'key_2': 'other_value',
                                      'key_3': 'new_value'})

        assert datavars.namespace_1.var_1.key_1 == 'another_value'
        assert datavars.namespace_1.var_1.key_2 == 'other_value'
        assert datavars.namespace_1.var_1.key_3 == 'new_value'

    def test_if_hash_variable_is_created_as_fixed_and_then_changed_using_dictionary_with_a_new_key__the_hash_raises_the_VariableError_exception(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', type=HashType.fixed, source={'key_1': 'value_1',
                                                           'key_2': 'value_2'})

        assert datavars.namespace_1.var_1.key_1 == 'value_1'
        assert datavars.namespace_1.var_1.key_2 == 'value_2'

        with Namespace('namespace_1'):
            Variable('var_1', source={'key_1': 'another_value',
                                      'key_2': 'other_value',
                                      'key_3': 'new_value'})

        assert datavars.namespace_1['var_1'].fixed

        with pytest.raises(VariableError):
            datavars.namespace_1.var_1.key_1

    def test_if_variable_is_created_with_dependence_and_one_of_arguments_of_a_depend_function_is_whole_hash__the_variable_will_set_this_argument_as_dictionary_of_the_whole_hash_in_a_depend_function(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', source='value_1', type=StringType)
            Variable('var_2', type=HashType, source={'key_1': 'value_1',
                                                     'key_2': 'value_2'})
            Variable('var_3', source='value_3', type=StringType)

            def depend_hash(arg_1):
                return arg_1.value['key_1']
            Variable('var_4', type=StringType,
                     source=Dependence('.var_2', depend=depend_hash))

        assert datavars.namespace_1.var_4 == 'value_1'

    def test_if_a_variable_has_TableType_and_created_using_list_of_dicts_in_its_source_and_other_variable_created_using_depend_function_with_the_first_variable_in_its_arg__the_fitst_variable_is_created_with_Table_value_and_depend_function_of_the_second_variable_can_use_whole_table_for_the_calculating_of_the_variable_s_value(self):
        Namespace.reset()
        datavars = Namespace.datavars

        with Namespace('namespace_1'):
            Variable('var_1', type=TableType, source=[{'name': 'name_1',
                                                       'value': 'value_1'},
                                                      {'name': 'name_2',
                                                       'value': 'value_2'},
                                                      {'name': 'name_3',
                                                       'value': 'value_3'}])

            def depend_function(table):
                for row in table.value:
                    if row['name'] == 'name_2':
                        return row['value']
            Variable('var_2', type=StringType,
                     source=Dependence('namespace_1.var_1',
                                       depend=depend_function))

        assert datavars.namespace_1.var_2 == 'value_2'

    def test_if_variable_of_TableType_is_created_using_dependence_that_creates_table__the_created_variable_has_generated_table_as_its_value(self):
        try:
            from portage.package.ebuild.config import config
            value = [{'name': name, 'path': path}
                     for path, name in
                     config().repositories.location_map.items()]
            Namespace.reset()
            datavars = Namespace.datavars

            with Namespace('main'):
                Variable('chroot', source='/', type=StringType.readonly)

            with Namespace('os'):
                def source_function(chroot_path):
                    if chroot_path.value == '/':
                        with stderr_devnull():
                            return config()
                Variable('config', source=Dependence('main.chroot',
                                                     depend=source_function),
                         type=VariableType)

                def config_source(config):
                    return [{'name': name, 'path': path}
                            for path, name in
                            config.value.repositories.location_map.items()]
                Variable('repositories', type=TableType,
                         source=Dependence('.config', depend=config_source))

            assert datavars.os.repositories == value
        except ImportError:
            # Если тестируем на другой системе модуль portage будет не найден.
            assert True

    # Тестируем теперь заполнитель переменных из calculate.ini
    def test_if_calculate_ini_file_is_used_just_to_create_some_variables__the_namespace_filler_will_create_them(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller(restrict_creation=False)

        calculate_ini_text = """
[os]
test = 123

[os][linux]
shortname = CLD
fullname = Calculate Linux Desktop
"""
        namespace_filler.fill(datavars, calculate_ini_text)
        assert datavars.os.test == '123'
        assert datavars.os.linux.shortname == 'CLD'
        assert datavars.os.linux.fullname == 'Calculate Linux Desktop'

    def test_if_a_calculate_ini_file_is_used_to_create_some_variables_and_an_other_one_is_used_to_update_value_of_the_variable__the_namespace_filler_creates_them_using_a_first_calculate_ini_and_then_changes_value_using_a_second_calculate_ini(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller(restrict_creation=False)

        first_ini_text = """
[os]
test = 123

[os][linux]
shortname = CLD
fullname = Calculate Linux Desktop
"""
        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.os.test == '123'
        assert datavars.os.linux.shortname == 'CLD'
        assert datavars.os.linux.fullname == 'Calculate Linux Desktop'

        second_ini_text = """
[os][linux]
shortname = CLDX
        """
        namespace_filler.fill(datavars, second_ini_text)
        assert datavars.os.linux.shortname == 'CLDX'

    def test_if_calculate_ini_file_creates_a_variable_and_then_uses_append_operation_to_append_any_value_to_the_created_variable__the_namespace_filler_appends_the_value_to_the_created_variable(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller(restrict_creation=False)
        first_ini_text = """
[os]
test = 123

[os]
test += 345
"""
        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.os.test == "123,345"

        second_ini_text = """
[os]
test += asdf,qwer,zxcv
"""

        namespace_filler.fill(datavars, second_ini_text)
        assert datavars.os.test == "123,345,asdf,qwer,zxcv"

    def test_if_calculate_ini_file_creates_a_variable_and_then_uses_remove_operation_to_remove_any_values_in_the_created_variable__the_namespace_filler_removes_the_values_from_the_created_variable(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller(restrict_creation=False)
        first_ini_text = """
[os]
test = 123,345,asdf,qwer,zxcv
"""
        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.os.test == "123,345,asdf,qwer,zxcv"

        second_ini_text = """
[os]
test -= 345
"""

        namespace_filler.fill(datavars, second_ini_text)
        assert datavars.os.test == "123,asdf,qwer,zxcv"

        third_ini_text = """
[os]
test -= asdf,zxcv
"""

        namespace_filler.fill(datavars, third_ini_text)
        assert datavars.os.test == "123,qwer"

    def test_if_calculate_ini_file_is_used_for_creation_variables_in_the_custom_and_some_other_namespaces_and_for_changing_of_a_variable_from_not_custom_namespaces__the_NamespaceIniFiller_object_just_creates_variables_in_the_custom_namespace_and_modifies_variables_from_other_namespaces(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('var_1', type=StringType, source='value_1')
            Variable('var_2', type=StringType, source='value_2')

        first_ini_text = """
[os]
var_1 = other_value
var_3 = value_3

[custom][test_1]
var_1 = value_1

[custom][test_2]
var_1 = value_1
"""

        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.os.var_1 == "other_value"
        assert 'var_3' not in datavars.os
        assert datavars.custom.test_1.var_1 == "value_1"
        assert datavars.custom.test_2.var_1 == "value_1"

    def test_if_calculate_ini_file_is_used_for_clearing_namespaces_in_the_custom_and_some_other_namespaces__the_NamespaceIniFiller_object_clears_namespaces_only_from_the_custom_namespace(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('var_1', type=StringType, source='value_1')
            Variable('var_2', type=StringType, source='value_2')

        first_ini_text = """
[custom][test]
var_1 = value_1
var_2 = value_2
"""

        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.custom.test.var_1 == 'value_1'
        assert datavars.custom.test.var_2 == 'value_2'

        second_ini_text = """
[custom][test][]

[os][]
"""

        namespace_filler.fill(datavars, second_ini_text)
        assert datavars.os.var_1 == 'value_1'
        assert datavars.os.var_2 == 'value_2'
        assert 'var_1' not in datavars.custom.test.variables
        assert 'var_2' not in datavars.custom.test.variables

    def test_if_calculate_ini_file_contains_some_errors__the_NamespaceIniFiller_object_collect_all_error_messages_to_the_error_attribute(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        input_text = '''[section
varval1 = value1
varval2 = value2
[section][sub2]
varval1 = value1
eee

[section2
varval4 = value4
[section][]
[section][][sub]
[section][][sub][]'''

        if isinstance(datavars, Datavars):
            errors = ['SyntaxError:1: [section',
                      'SyntaxError:2: varval1 = value1',
                      'SyntaxError:3: varval2 = value2',
                      ("VariableError:4: variables package 'section' is not"
                       " found."),
                      'SyntaxError:6: eee',
                      'SyntaxError:8: [section2',
                      ("VariableError:10: can not clear namespace 'section'."
                       " Variables package 'section' is not found."),
                      'SyntaxError:11: [section][][sub]',
                      'SyntaxError:12: [section][][sub][]']
        else:
            errors = ['SyntaxError:1: [section',
                      'SyntaxError:2: varval1 = value1',
                      'SyntaxError:3: varval2 = value2',
                      ("VariableError:4: can not create namespace"
                       " '<root>.section' in not 'custom' namespace."),
                      'SyntaxError:6: eee',
                      'SyntaxError:8: [section2',
                      ("VariableError:10: can not clear namespace 'section'."
                       " Namespace is not found."),
                      'SyntaxError:11: [section][][sub]',
                      'SyntaxError:12: [section][][sub][]']

        namespace_filler.fill(datavars, input_text)
        for parsed_error, error in zip(namespace_filler.errors, errors):
            assert parsed_error == error

    def test_if_a_variable_is_created_as_readonly_variable_and_there_is_a_try_to_new_value_using_ini_file__the_ini_parser_does_not_change_the_variable_and_sets_variable_error(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('var_1', source='value_1', type=StringType.readonly)
            Variable('var_2', type=HashType.readonly,
                     source={'key_1': 'value_1', 'key_2': 'value_2'})

        first_ini_text = """[os]
var_1 = new_value

[os][var_2]
key_1 = new_value
"""

        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.os.var_1 == 'value_1'
        assert datavars.os.var_2.get_hash() == {'key_1': 'value_1',
                                                'key_2': 'value_2'}
        assert namespace_filler.errors == ["VariableError:2: can not change"
                                           " readonly variable 'os.var_1'",
                                           "VariableError:5: can not change"
                                           " readonly hash variable 'os.var_2'"
                                           ]

    def test_if_calculate_ini_file_is_used_for_changing_of_an_unfixed_hash_variable_created_using_the_variables_api_and_for_adding_of_the_new_key_in_it__the_NamespaceIniFiller_object_changes_hash_value_and_adds_a_new_key(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('var_1', type=HashType, source={'key_1': 'value_1',
                                                     'key_2': 'value_2'})
        first_ini_text = """
[os][var_1]
key_1 = new_value
key_3 = value_3
"""

        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.os.var_1.get_hash() == {'key_1': 'new_value',
                                                'key_2': 'value_2',
                                                'key_3': 'value_3'}

    def test_if_calculate_ini_file_is_used_just_for_changing_of_an_fixed_hash_variable_created_using_the_variables_api__the_NamespaceIniFiller_object_changes_hash_value(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('var_1', type=HashType.fixed, source={'key_1': 'value_1',
                                                           'key_2': 'value_2'})
        first_ini_text = """
[os][var_1]
key_1 = new_value
key_2 = weird_value
"""

        namespace_filler.fill(datavars, first_ini_text)
        print('NEW HASH VALUE: {}'.format(datavars.os.var_1.get_hash()))
        assert datavars.os.var_1.get_hash() == {'key_1': 'new_value',
                                                'key_2': 'weird_value'}

    def test_if_calculate_ini_file_is_used_just_for_adding_a_new_key_to_an_fixed_hash_variable_created_using_the_variables_api__the_NamespaceIniFiller_object_raises_the_VariableError_exception(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('var_1', type=HashType.fixed, source={'key_1': 'value_1',
                                                           'key_2': 'value_2'})
        first_ini_text = """
[os][var_1]
key_3 = weird_value
"""

        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.os.var_1.get_hash() == {'key_1': 'value_1',
                                                'key_2': 'value_2'}

    def test_if_calculate_ini_file_is_used_for_creation_of_the_table_variable_in_the_custom_namespace__the_NamespaceIniFiller_object_creates_table_in_the_custom_namespace(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('var_1', type=StringType, source='value_1')
            Variable('var_2', type=StringType, source='value_2')

        first_ini_text = """
[custom][test][0]
name = name_0
value = value_0

[custom][test][1]
name = name_1
value = value_1

[custom][test][2]
name = name_2
value = value_2
"""
        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.custom.test ==\
            [{'name': 'name_0', 'value': 'value_0'},
             {'name': 'name_1', 'value': 'value_1'},
             {'name': 'name_2', 'value': 'value_2'}]

    def test_if_calculate_ini_file_is_used_for_creation_of_the_table_variable_in_not_custom_namespaces__the_NamespaceIniFiller_object_does_not_create_anything(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('var_1', type=StringType, source='value_1')
            Variable('var_2', type=StringType, source='value_2')

        first_ini_text = """
[os][test][0]
name = name_0
value = value_0

[os][test][1]
name = name_1
value = value_1

[os][test][2]
name = name_2
value = value_2
"""

        namespace_filler.fill(datavars, first_ini_text)
        assert 'test' not in datavars.os

    def test_if_two_tables_are_created_using_calculate_ini_file_and_variables_api_and_then_calculate_ini_file_is_used_for_clearing_and_add_rows_in_the_both_tables__the_NamespaceIniFiller_object_clears_tables_and_adds_rows(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('os'):
            Variable('test', type=TableType, source=[{'name': 'name_1',
                                                      'value': 'value_1'},
                                                     {'name': 'name_2',
                                                      'value': 'value_2'},
                                                     {'name': 'name_3',
                                                      'value': 'value_3'}])

        first_ini_text = """
# Создаем таблицу.
[custom][test][0]
name = name_0
value = value_0

[custom][test][1]
name = name_1
value = value_1

# Очищаем таблицу.
[custom][test][]

# Добавляем в таблицу строку.
[custom][test][0]
name = name_0
value = value_0

# Очищаем таблицу созданную интерфейсом.
[os][test][]

# Добавляем в эту таблицу строку.
[os][test][0]
name = strange_name
value = weird_value
"""

        namespace_filler.fill(datavars, first_ini_text)

        assert datavars.custom.test ==\
            [{'name': 'name_0', 'value': 'value_0'}]
        assert datavars.os.test ==\
            [{'name': 'strange_name', 'value': 'weird_value'}]

    def test_if_calculate_ini_file_is_used_for_modifying_of_the_table_from_calculate_ini_file__the_NamespaceIniFiller_object_modifies_the_table(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        first_ini_text = """
# Создаем таблицу.
[custom][test][0]
name = name_0
value = value_0

[custom][test][1]
name = name_1
value = value_1
"""

        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.custom.test ==\
            [{'name': 'name_0', 'value': 'value_0'},
             {'name': 'name_1', 'value': 'value_1'}]

        second_ini_text = """
[custom][test][1]
name = other_name
value = another_value
"""

        namespace_filler.fill(datavars, second_ini_text)
        assert datavars.custom.test ==\
            [{'name': 'name_0', 'value': 'value_0'},
             {'name': 'other_name', 'value': 'another_value'}]

    def test_if_calculate_ini_file_is_used_for_modifying_of_the_table_created_using_variables_api__the_NamespaceIniFiller_object_modifies_the_table(self):
        Namespace.reset()
        datavars = Namespace.datavars
        namespace_filler = NamespaceIniFiller()

        with Namespace('namespace_1'):
            Variable('test', type=TableType, source=[{'name': 'name_1',
                                                      'value': 'value_1'},
                                                     {'name': 'name_2',
                                                      'value': 'value_2'},
                                                     {'name': 'name_3',
                                                      'value': 'value_3'}])

        first_ini_text = """
[namespace_1][test][0]
name = new_name
value = other_value

[namespace_1][test][1]
name = common_name
value = another_value
"""

        namespace_filler.fill(datavars, first_ini_text)
        assert datavars.namespace_1.test ==\
            [{'name': 'new_name', 'value': 'other_value'},
             {'name': 'common_name', 'value': 'another_value'},
             {'name': 'name_3', 'value': 'value_3'}]

    # Теперь тестируем применение объекта Datavars, через который
    # осуществляется доступ к переменным и их загрузка.
    def test_if_a_Datavars_object_is_created_with_path_to_the_variables_without_any_Dependencies_and_then_used_to_get_access_to_the_some_variables_from__the_datavars_object_dynamically_loads_variables_and_retruns_necessary_variables(self):
        datavars = Datavars(
                        variables_path='tests/variables/testfiles/variables_0',
                        repository_map={})

        assert datavars.os.ns.var_1 == 'value_1'
        assert datavars.os.ns.var_2 == 2
        assert 'main' not in datavars.root

        assert datavars.main.strange_variable == 'weird_value'
        assert datavars.main.plain_variable is True
        assert 'main' in datavars.root

    def test_if_a_Datavars_object_is_created_with_path_to_the_variables_with_some_Dependencies_and_then_used_to_get_access_to_the_some_variables_from__the_datavars_object_dynamically_loads_variables_and_retruns_necessary_variables(self):
        datavars = Datavars(
                        variables_path='tests/variables/testfiles/variables_1',
                        repository_map={})

        assert datavars.main.chroot == '/'
        assert 'os' not in datavars.root

        assert datavars.os.calculate == 'test1 test2'
        assert 'os' in datavars.root

        assert datavars.level.level_2.vargetter == '/ test'
        assert 'main' in datavars.root

        assert datavars.level.simple == "simple value"
        assert datavars.level.use_local_simple == "Using simple value"
        assert datavars.level.use_full_simple == "Using simple value"
        assert datavars.level.device_child == "hdd"
        assert datavars.level.device == [{"dev": "/dev/sda",
                                          "type": "hdd",
                                          "name": "Samsung SSD"},
                                         {"dev": "/dev/sdb",
                                          "type": "flash",
                                          "name": "Transcend 64GB"}]

    def test_if_a_Datavars_object_is_created_with_path_to_the_variables_with_Dependencies_and_then_the_variables_are_changed_using_calculate_ini_files_and_the_datavars_object_used_to_get_access_to_the_some_variables_from__the_datavars_object_dynamically_loads_variables_and_retruns_necessary_variables(self):
        datavars = Datavars(
                        variables_path='tests/variables/testfiles/variables_4',
                        repository_map={})

        assert datavars.main.chroot == '/'
        assert 'os' not in datavars.root

        assert datavars.level.level_2.vargetter == '/ test'
        assert 'main' in datavars.root

        assert datavars.level.simple == "simple value"

        datavars._loader.fill_from_custom_ini(os.path.join(TESTFILES_PATH,
                                                           'calculate.ini'))

        assert 'os' in datavars.root
        assert datavars.level.simple == 'weird value'
        assert datavars.main.chroot == '/any/other/path'
        assert datavars.level.level_2.vargetter == '/any/other/path test'

    def test_to_make_testfiles(self):
        shutil.copytree(os.path.join(TESTFILES_PATH, 'gentoo.backup'),
                        os.path.join(TESTFILES_PATH, 'gentoo'),
                        symlinks=True)
        assert os.path.exists(os.path.join(TESTFILES_PATH, 'gentoo'))

        shutil.copytree(os.path.join(TESTFILES_PATH, 'ini_vars.backup'),
                        os.path.join(TESTFILES_PATH, 'ini_vars'))
        assert os.path.exists(os.path.join(TESTFILES_PATH, 'ini_vars'))

    def test_test_if_Datavars_object_is_created_with_a_specific_variables_path_and_repository_map_is_set_and_profile_path_is_set_in_the_variables__the_Datavars_object_finds_all_variables_packages_and_ini_files_from_a_profile_using_the_profile_path_variable_and_the_repository_map(self):
        repository_map = {'distros': os.path.join(TESTFILES_PATH,
                                                  "gentoo/repos/distros"),
                          'calculate': os.path.join(TESTFILES_PATH,
                                                    "gentoo/repos/calculate"),
                          'gentoo': os.path.join(TESTFILES_PATH,
                                                 "gentoo/portage")}

        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH, 'variables_2'),
                    repository_map=repository_map)

        assert datavars.os.linux.shortname == 'CLD'
        assert datavars.os.linux.fullname == 'Calculate Linux Desktop'
        assert datavars.os.linux.ver == '20'
        assert datavars.os.linux.subname == 'KDE'
        assert datavars.os.linux.arch == 'amd64'

        assert datavars.os.hashvar.value1 == 'new1'
        assert datavars.os.hashvar.value2 == 'new2'

        assert datavars.os.tablevar == [
                               {'dev': '/dev/sda1', 'mount': 'swap'},
                               {'dev': '/dev/sda2', 'mount': '/'},
                               {'dev': '/dev/sda5', 'mount': '/var/calculate'}]

    def test_test_if_Datavars_object_is_created_with_a_specific_variables_path_and_the_repository_map_and_the_current_profile_path_is_set_in_the_variables__the_Datavars_object_finds_all_variables_packages_and_ini_files_from_a_profile_using_the_profile_path_variable_and_the_repositories_variable(self):
        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH, 'variables_3'))

        assert datavars.os.linux.shortname == 'CLD'
        assert datavars.os.linux.fullname == 'Calculate Linux Desktop'
        assert datavars.os.linux.ver == '20'
        assert datavars.os.linux.subname == 'KDE'
        assert datavars.os.linux.arch == 'amd64'

        assert datavars.os.hashvar.value1 == 'new1'
        assert datavars.os.hashvar.value2 == 'new2'

        assert datavars.os.tablevar == [
                               {'dev': '/dev/sda1', 'mount': 'swap'},
                               {'dev': '/dev/sda2', 'mount': '/'},
                               {'dev': '/dev/sda5', 'mount': '/var/calculate'}]

    def test_test_if_Datavars_object_is_created_with_a_specific_variables_path_and_the_repository_map_the_current_profile_path_and_some_ini_files_paths_is_set_in_the_variables__the_Datavars_object_finds_all_variables_packages_and_ini_files_from_a_profile_using_the_profile_path_variable_and_the_repositories_variable_and_loads_variables_from_ini_files_from_the_env_order_and_the_env_path_variables(self):
        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH, 'variables_5'))
        assert datavars.system.env_order == ['system', 'local']
        assert datavars.system.env_path.get_hash() == {
                                            'system':
                                            os.path.join(
                                                   TESTFILES_PATH,
                                                   'ini_vars/calculate_0.ini'),
                                            'local':
                                            os.path.join(
                                                   TESTFILES_PATH,
                                                   'ini_vars/calculate_1.ini')}
        assert datavars.custom.ns.var_1 == '1'
        assert datavars.custom.ns.var_2 == 'weird value'
        assert datavars.custom.ns.var_3 ==\
            '/the/true/path/to/eternity/in/the/abyss'
        assert datavars.custom.ns.var_4 == '12'

        assert datavars.os.linux.test == 'new value'
        assert datavars.os.linux.test_var == 'test = new value'

        assert datavars.os.hashvar.value1 == 'new1'
        assert datavars.os.hashvar.value2 == 'new2'

        assert datavars.os.calculate == 'new1 new2'

    def test_if_Datavars_object_is_created_and_all_variables_are_loaded__the_set_method_can_be_used_for_changing_just_variable_s_values_and_save_their_sources_and_the_reset_method_can_return_values_from_source(self):
        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH, 'variables_6'))
        datavars.os.linux['test'].set('other value')
        datavars.os['hashvar'].set({'value1': 'other1',
                                    'value2': 'other2'})

        assert datavars.os.linux['test'].set_by_user
        assert datavars.os['hashvar'].set_by_user

        assert datavars.os.linux.test == 'other value'
        assert datavars.os.hashvar.value1 == 'other1'
        assert datavars.os.hashvar.value2 == 'other2'

        assert datavars.os.linux.test_var == 'test = other value'
        assert datavars.os.calculate == 'other1 other2'

        datavars.os['calculate'].set('special value')
        assert datavars.os.calculate == 'special value'

        datavars.os['hashvar'].set({'value1': 'weird1',
                                    'value2': 'weird2'})
        assert datavars.os.hashvar.value1 == 'weird1'
        assert datavars.os.hashvar.value2 == 'weird2'

        # При изменении зависимости эта переменная не инвалидируется, потому
        # что была задана пользователем. Поэтому ее значение установленное set
        # сохраняется.
        assert datavars.os.calculate == 'special value'

        # Если сбросить значения будут пересчитаны, используя установленный
        # источник значения.
        datavars.os['calculate'].reset()
        assert datavars.os.calculate == 'weird1 weird2'

        datavars.os.linux['test'].reset()
        assert datavars.os.linux.test == 'new value'

        datavars.os['hashvar'].reset()
        assert datavars.os.hashvar.value1 == 'new1'
        assert datavars.os.hashvar.value2 == 'new2'

        assert datavars.os.calculate == 'new1 new2'

    def test_if_Datavars_object_is_created_and_all_variables_are_loaded__the_set_method_can_be_used_for_changing_hash_values_with_saving_of_their_sources(self):
        datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
                                                        'variables_6'))
        datavars.os.hashvar['value1'].set('old but gold')
        assert datavars.os.hashvar.get_hash() == {'value1': 'old but gold',
                                                  'value2': 'new2'}

        datavars.os['hashvar'].reset()
        assert datavars.os.hashvar.get_hash() == {'value1': 'new1',
                                                  'value2': 'new2'}

        datavars.os.hashvar['value2'].set('almost blue')
        assert datavars.os.hashvar.get_hash() == {'value1': 'new1',
                                                  'value2': 'almost blue'}

        datavars.os.hashvar['value2'].reset()
        assert datavars.os.hashvar.get_hash() == {'value1': 'new1',
                                                  'value2': 'new2'}

    # Теперь тестируем применение объекта Datavars в шаблонах.
    def test_if_Datavars_object_is_set_as_the_datavars_for_the_TemplateEngine_object__variables_from_the_Datavars_object_can_be_inserted_in_the_processed_template(self):
        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH, 'variables_7'))
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=TESTFILES_PATH,
                                         datavars_module=datavars)
        input_template = '''{% calculate name = 'filename', force -%}
os.linux.test = {{ os.linux.test }}
os.hashvar.value1 = {{ os.hashvar.value1 }}
os.hashvar.value2 = {{ os.hashvar.value2 }}
os.calculate = {{ os.calculate }}'''
        output_text = '''os.linux.test = new value
os.hashvar.value1 = new1
os.hashvar.value2 = new2
os.calculate = new1 new2'''

        template_engine.process_template_from_string(input_template, FILE)
        text = template_engine.template_text
        assert text == output_text

    def test_if_Datavars_object_is_set_as_the_datavars_for_the_TemplateEngine_object_and_save_tag_is_used_for_saving_some_variables_and_target_file_is_not_set_for_the_save_tag__the_Datavars_object_saves_and_modifies_variables_from_the_save_tag(self):
        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH, 'variables_7'))
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=TESTFILES_PATH,
                                         datavars_module=datavars)
        input_template_1 = '''{% calculate name = 'filename', force -%}
{% save custom.ns.value = 12 -%}
{% save os.linux.test = 'new value' %}
{% save os.hashvar = {'value1': 'weird1', 'value2': 'weird2'} %}'''

        template_engine.process_template_from_string(input_template_1, FILE)
        assert datavars.custom.ns.value == '12'

        assert datavars.os.linux.test == 'new value'
        assert datavars.os.linux.test_var == 'test = new value'

        assert datavars.os.hashvar.value1 == 'weird1'
        assert datavars.os.hashvar.value2 == 'weird2'
        assert datavars.os.calculate == 'weird1 weird2'

        input_template_2 = '''{% calculate name = 'filename', force -%}
{% save os.hashvar.value1 = 'new1' %}'''
        template_engine.process_template_from_string(input_template_2, FILE)

        assert datavars.os.hashvar.value1 == 'new1'
        assert datavars.os.hashvar.value2 == 'weird2'
        assert datavars.os.calculate == 'new1 weird2'

    def test_if_Datavars_object_is_set_as_the_datavars_for_the_TemplateEngine_object_and_save_tag_is_used_for_appending_and_removing_some_variable_s_values_and_target_file_is_not_set_for_the_save_tag__the_Datavars_object_changes_variables_from_the_save_tag(self):
        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH, 'variables_8'))
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=TESTFILES_PATH,
                                         datavars_module=datavars)

        input_template_1 = '''{% calculate name = 'filename', force -%}
{% save custom.ns.var_1 += 12 -%}
{% save os.linux.test_1 += 10 -%}
{% save os.linux.test_2 += 12 -%}
{% save os.linux.test_3 += 'd' -%}
{% save os.linux.test_4 += [4, 5] -%}
{% save custom.ns.var_2 += 'val4,val5' -%}
'''

        template_engine.process_template_from_string(input_template_1, FILE)

        assert datavars.custom.ns.var_1 == '1,12'
        assert datavars.os.linux.test_1 == 22
        assert datavars.os.linux.test_2 == 13.2
        assert datavars.os.linux.test_3 == ['a', 'b', 'c', 'd']
        assert datavars.os.linux.test_4 == [1, 2, 3, 4, 5]
        assert datavars.custom.ns.var_2 == 'val1,val2,val3,val4,val5'

        input_template_2 = '''{% calculate name = 'filename', force -%}
{% save custom.ns.var_1 -= 1 -%}
{% save os.linux.test_1 -= 11 -%}
{% save os.linux.test_2 -= 2.1 -%}
{% save os.linux.test_3 -= 'a' -%}
{% save os.linux.test_4 -= [1, 3] -%}
{% save custom.ns.var_2 -= 'val1,val4' -%}
'''

        template_engine.process_template_from_string(input_template_2, FILE)

        assert datavars.custom.ns.var_1 == '12'
        assert datavars.os.linux.test_1 == 11
        assert datavars.os.linux.test_2 == 11.1
        assert datavars.os.linux.test_3 == ['b', 'c', 'd']
        assert datavars.os.linux.test_4 == [2, 4, 5]
        assert datavars.custom.ns.var_2 == 'val2,val3,val5'

    def test_if_Datavars_object_is_set_as_the_datavars_for_the_TemplateEngine_object_and_save_tag_is_used_for_changing_some_variable_s_values_and_target_file_is_set_for_the_save_tag__the_Datavars_object_creates_new_and_modifies_existing_variables_and_saves_them_to_the_target_files(self):
        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH, 'variables_9'))
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=TESTFILES_PATH,
                                         datavars_module=datavars)

        input_template_1 = '''{% calculate name = 'filename', force -%}
{% save.local custom.ns.var_3 = 'value' -%}
{% save.local custom.ns.var_2 += 'val4' -%}
{% save.system custom.ns.var_5 = '/highway/to/hell' -%}
'''

        local_ini_result = '''[custom][ns]
var_1 = 1
var_2 = val1,val2,val3,val4
var_3 = value

[os][linux]
test_1 += 12'''

        system_ini_result = '''[custom][ns]
var_5 = /highway/to/hell
var_6 = 12

[os][linux]
test = new value'''

        template_engine.process_template_from_string(input_template_1, FILE)
        datavars.save_variables()

        assert local_ini_result == read_file(
                                       datavars.system.env_path['local'].value)
        assert system_ini_result == read_file(
                                      datavars.system.env_path['system'].value)

    def test_if_Datavars_object_is_set_as_the_datavars_for_the_TemplateEngine_object_and_save_tag_is_used_for_changing_some_hash_variable_s_values_and_target_file_is_set_for_the_save_tag__the_Datavars_object_modifies_hash_variables_and_saves_them_to_the_target_files_as_namespaces(self):
        datavars = Datavars(
                   variables_path=os.path.join(TESTFILES_PATH, 'variables_10'))
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=TESTFILES_PATH,
                                         datavars_module=datavars)

        input_template_1 = '''{% calculate name = 'filename', force -%}
{% save.system os.hashvar_0 = {'value1': 'new1', 'value2': 'new2'} -%}
{% save.system os.hashvar_1.key1 = 'weird1' -%}

{% save.system os.hashvar_2.id_1 = 1575 -%}
{% save.system os.hashvar_2.id_2 = 1349 -%}
'''

        system_ini_result = '''[os][hashvar_0]
value1 = new1
value2 = new2

[os][hashvar_1]
key1 = weird1

[os][hashvar_2]
id_1 = 1575
id_2 = 1349'''

        template_engine.process_template_from_string(input_template_1, FILE)
        datavars.save_variables()
        assert system_ini_result ==\
            read_file(datavars.system.env_path['system'].value)

    # Теперь тестируем обработку ошибок.
    def test_if_calculate_ini_file_has_some_syntax_errors_in_its_text__loader_skips_incorrect_lines_and_puts_some_error_messages_in_the_output(self):
        test_messages = []
        test_handler = LoggingHandler(test_messages)
        test_logger = logging.getLogger("test")
        test_logger.addHandler(test_handler)

        Datavars(variables_path=os.path.join(TESTFILES_PATH, 'variables_12'),
                 logger=test_logger)

        messages = [('ERROR', 'SyntaxError:1: [section'),
                    ('ERROR', 'SyntaxError:2: varval1 = value1'),
                    ('ERROR', 'SyntaxError:3: varval2 = value2'),
                    ('ERROR', ("VariableError:4: variables package 'section'"
                               " is not found.")),
                    ('ERROR', 'SyntaxError:6: eee'),
                    ('ERROR', 'SyntaxError:8: [section2'),
                    ('ERROR', ("VariableError:10: can not clear namespace"
                               " 'section'. Variables package 'section' is not"
                               " found.")),
                    ('ERROR', 'SyntaxError:11: [section][][sub]'),
                    ('ERROR', 'SyntaxError:12: [section][][sub][]'),
                    ('WARNING', 'Some variables was not loaded.'),
                    ('INFO', "Variables from 'system' are loaded"),
                    ('INFO', "Loading variables from file: 'local'"),
                    ('WARNING', ("File 'local' is not found. Variables"
                                 " are not loaded"))]

        print('logged messages:', test_messages)
        for message, datavars_message in zip(
                                          messages,
                                          test_messages[-len(messages):]):
            assert message == datavars_message

    def test_if_any_variables_module_has_syntax_error_in_its_code__the_variables_loader_raises_the_VariableError_exception(self):
        with pytest.raises(VariableError):
            Datavars(variables_path=os.path.join(TESTFILES_PATH,
                     'variables_13'))

    def test_if_datavars_object_is_used_to_get_access_to_an_unexisting_variable_from_a_not_custom_namespace_and_from_a_custom_namespace__the_datavars_module_raises_the_VariableNotFound_exception_in_the_first_case_and_returns_None_value_in_the_second_case(self):
        datavars = Datavars(variables_path=os.path.join(TESTFILES_PATH,
                                                        'variables_14'))

        with pytest.raises(VariableNotFoundError):
            datavars.level.variable

        assert datavars.custom.variable is None

    def test_if_some_template_processed_by_the_template_engine_tries_to_get_value_of_a_variable_that_does_not_exist__the_template_engine_raises_the_VariableNotFound_error(self):
        datavars = Datavars(
                    variables_path=os.path.join(TESTFILES_PATH,
                                                'variables_15'))
        template_engine = TemplateEngine(appends_set=APPENDS_SET,
                                         chroot_path=TESTFILES_PATH,
                                         datavars_module=datavars)
        input_template = '''{% calculate name = 'filename', force -%}
os.linux.test_3 = {{ os.linux.test_3 }}
'''

        with pytest.raises(VariableNotFoundError):
            template_engine.process_template_from_string(input_template, FILE)

    def test_two_datavars_objects(self):
        datavars_1 = Datavars(variables_path=os.path.join(TESTFILES_PATH,
                                                          'variables_7'))
        datavars_2 = Datavars(variables_path=os.path.join(TESTFILES_PATH,
                                                          'variables_7'))

        input_template_1 = '''{% calculate name = 'filename', force -%}
{%- save os.linux.test = 'new_first' -%}'''
        input_template_2 = '''{% calculate name = 'filename', force -%}
{%- save os.linux.test = 'new_second' -%}'''

        template_engine_1 = TemplateEngine(appends_set=APPENDS_SET,
                                           chroot_path=TESTFILES_PATH,
                                           datavars_module=datavars_1)
        template_engine_2 = TemplateEngine(appends_set=APPENDS_SET,
                                           chroot_path=TESTFILES_PATH,
                                           datavars_module=datavars_2)

        template_engine_1.process_template_from_string(input_template_1, FILE)
        template_engine_2.process_template_from_string(input_template_2, FILE)

        assert datavars_1.os.linux.test == 'new_first'
        assert datavars_2.os.linux.test == 'new_second'

    def test_for_removing_testfiles(self):
        shutil.rmtree(os.path.join(TESTFILES_PATH, 'gentoo'))
        assert not os.path.exists(os.path.join(TESTFILES_PATH, 'gentoo'))

        shutil.rmtree(os.path.join(TESTFILES_PATH, 'ini_vars'))
        assert not os.path.exists(os.path.join(TESTFILES_PATH, 'ini_vars'))
