commit fce26672318c235b81c36f48dc134b6834259142 Author: Хирецкий Михаил Date: Thu Feb 6 12:41:03 2020 +0300 Added formats diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b942044 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +install: + sudo python ./setup.py install --single-version-externally-managed --root=/ + +test: install + pytest diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..e69de29 diff --git a/calculate/__init__.py b/calculate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calculate/templates/__init__.py b/calculate/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calculate/templates/format/__init__.py b/calculate/templates/format/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calculate/templates/format/base_format.py b/calculate/templates/format/base_format.py new file mode 100644 index 0000000..891d759 --- /dev/null +++ b/calculate/templates/format/base_format.py @@ -0,0 +1,189 @@ +# vim: fileencoding=utf-8 +# +from collections import OrderedDict +from jinja2 import Environment, PackageLoader +from pprint import pprint +try: + from lxml.etree.ElementTree import fromstring +except ImportError: + from xml.etree.ElementTree import fromstring + + +class BaseFormat(): + def __init__(self, processing_methods): + self._processing_methods = processing_methods + self._document_dictionary = OrderedDict() + self._item_to_add = OrderedDict() + self._format = 'none' + + self.TEMPLATES_DIRECTORY = 'templates' + + self._fatal_error_flag = False + self._ready_to_update = False + self._match = False + + self._need_finish = False + self._comments_processing = False + + self._join_before = False + self._join_before_in_areas = False + + # для отладки. + self._line_timer = 0 + + def _lines_to_dictionary(self, document_lines): + # print('Lines processing...') + for line in document_lines: + # print(self._line_timer, '\t', line) + for processing_method in self._processing_methods: + processing_method(line) + + if self._fatal_error_flag: + # Действия если файл невозможно разобрать. + print('Can not parse file.') + self._document_dictionary = OrderedDict() + return + + if self._is_match(): + if self._is_ready_to_update(): + self._document_dictionary.update(self._item_to_add) + break + else: + # Действия если не удалось разобрать строку. + print('Line', self._line_timer, + 'is not correct. Can not parse file.') + self._document_dictionary = OrderedDict() + return + + self._line_timer += 1 + + if self._need_finish: + self._finish_method() + + def _parse_xml_to_dictionary(self, xml_document_text): + root = fromstring(xml_document_text) + self._document_dictionary = self._processing_methods[root.tag](root) + + def print_dictionary(self): + pprint(self._document_dictionary) + + def join_template(self, template): + self._join(self._document_dictionary, + template._document_dictionary, + self._join_before) + + def _get_list_of_logic_lines(self, text): + list_of_lines = [] + lines_to_join = [] + for line in text.splitlines(): + line = line.strip() + if line == '': + continue + if not line.endswith("\\"): + lines_to_join.append(line) + joined_line = "".join(lines_to_join) + list_of_lines.append(joined_line) + lines_to_join = [] + else: + lines_to_join.append(line[:-1]) + return list_of_lines + + def _join(self, original, template, join_before): + if template == OrderedDict(): + return + if join_before: + forwarded_items = OrderedDict() + for key_value in template: + if key_value[0] == '!': + # Удаление соответствующего элемента из original. + if isinstance(key_value, tuple): + item_to_delete = ('',) + key_value[1:] + elif isinstance(key_value, str): + item_to_delete = key_value[1:] + + if item_to_delete in original.keys(): + original.pop(item_to_delete) + elif key_value[0] == '-': + # Замена соответствующего элемента из original. + if isinstance(key_value, tuple): + item_to_replace = ('',) + key_value[1:] + elif isinstance(key_value, str): + item_to_replace = key_value[1:] + + if item_to_replace not in original.keys(): + continue + + if isinstance(template[key_value], dict) and\ + template[key_value] == OrderedDict(): + original.pop(item_to_replace) + continue + + if self._comments_processing: + if '#' in original[item_to_replace]: + replaced = OrderedDict({'#': + original[item_to_replace]['#']} + ) + replaced.update(template[key_value]) + else: + replaced = template[key_value] + + original[item_to_replace] = replaced + else: + original[item_to_replace] = template[key_value] + + elif key_value not in original.keys(): + if isinstance(template[key_value], dict): + dictionary_to_add = OrderedDict() + self._join(dictionary_to_add, + template[key_value], + self._join_before_in_areas) + if dictionary_to_add != OrderedDict(): + if not join_before: + original[key_value] = dictionary_to_add + else: + forwarded_items[key_value] = dictionary_to_add + else: + if not join_before: + original[key_value] = template[key_value] + else: + forwarded_items[key_value] = template[key_value] + else: + if isinstance(original[key_value], dict) and \ + isinstance(template[key_value], dict): + self._join(original[key_value], + template[key_value], + self._join_before_in_areas) + else: + if self._comments_processing: + original[key_value][-1] = template[key_value][-1] + else: + original[key_value] = template[key_value] + if join_before: + for key_value in reversed(forwarded_items.keys()): + original[key_value] = forwarded_items[key_value] + original.move_to_end(key_value, last=False) + + def get_document_text(self): + file_loader = PackageLoader('calculate.templates.format', + self.TEMPLATES_DIRECTORY) + formats_environment = Environment(loader=file_loader, + trim_blocks=True, + lstrip_blocks=True) + formats_environment.globals.update(zip=zip) + formats_environment.add_extension('jinja2.ext.do') + template = formats_environment.get_template(self._format) + document_text = template.render( + document_dictionary=self._document_dictionary + ) + return document_text + + def _finish_method(self): + pass + + def _is_ready_to_update(self): + is_ready, self._match = self._ready_to_update, False + return is_ready + + def _is_match(self): + is_match, self._match = self._match, False + return is_match diff --git a/calculate/templates/format/bind_format.py b/calculate/templates/format/bind_format.py new file mode 100644 index 0000000..a78e93c --- /dev/null +++ b/calculate/templates/format/bind_format.py @@ -0,0 +1,293 @@ +# vim: fileencoding=utf-8 +# +# ToDo: добавить проверку того, полностью ли парсился документ. Если отпарсился +# не весь файл -- выдаем ошибку. +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import originalTextFor, OneOrMore, Word, alphanums, Literal,\ + ZeroOrMore, Forward, Optional, Group, restOfLine,\ + cppStyleComment, Keyword, printables, nums, SkipTo + + +class BINDFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [] + + super().__init__(processing_methods) + + self._ignore_comments = ignore_comments + self._comments_processing = True + self._format = 'bind' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + self._parse_text(document_text) + + def _initialize_parser(self): + left_brace = Literal('{') + right_brace = Literal('}') + semicolon = Literal(';') + action_symbols = (Literal('!') | + Literal('-')) + + plain_allow = Keyword('allow') + drop_allow = Keyword('!allow') + replace_allow = Keyword('-allow') + allow = (plain_allow | drop_allow | replace_allow) + keys = Keyword('keys') + inet = Keyword('inet') + + statement = originalTextFor( + Word(alphanums+'_-', + excludeChars='{};') + ) + + statement_name = originalTextFor( + Word(printables, + excludeChars='{};') + ) + + statement_class = originalTextFor( + Word(printables, + excludeChars='{};') + ) + + parameter_value = originalTextFor( + Word(printables, + excludeChars='{};') + )('parameter') + + ip_value = originalTextFor( + Word(nums+':./', + excludeChars=';{}') + ) + + # Будущий парсер блока. + block = Forward() + + # Для парсинга директивы inet_spec. + allow_item = (ip_value | statement_name) + semicolon.suppress() + key_item = statement_name + semicolon.suppress() + + allow_group = Group(Group(Optional(action_symbols, + default='')('action') + + allow) + + Group(left_brace.suppress() + + ZeroOrMore(allow_item) + + right_brace.suppress())) + + keys_group = Group(Group(Optional(action_symbols, default='')('action') + + keys) + + Group(left_brace.suppress() + + ZeroOrMore(key_item) + + right_brace.suppress())) + + inet_spec = (Group(Optional(action_symbols, default='')('action') + + inet + originalTextFor(SkipTo(allow, + include=False) + )('parameter') + )('name') + + Group(allow_group + + Optional(keys_group) + + semicolon.suppress())('content') + ).setParseAction( + self._add_inet_specline + ) + + # Для парсинга комментариев. + python_style_comment = originalTextFor(Literal('#') + restOfLine) + comments = (cppStyleComment | + python_style_comment).setParseAction( + self._create_comment_list + ) + + # Для парсинга директивы include. + include_line = (Optional(action_symbols, default='')('action') + + Keyword('include') + + Word(printables, excludeChars=';{}') + + Optional(semicolon.suppress()) + ).setParseAction( + self._add_include_line + ) + + # Для парсинга простых директив состоящих из одного + # или двух параметров. + plain_line = (Group(Optional(action_symbols, default='')('action') + + statement)('name') + + Optional(parameter_value) + + Optional(semicolon.suppress())).setParseAction( + self._add_plain_line + ) + + # Метод для парсинга IP адресов. + ip_line = (Group(Optional(action_symbols, default='')('action') + + ip_value + + Optional(semicolon.suppress()))('IP') + ).setParseAction(self._add_ipline) + + # Парсеры параметров. + param_line = (include_line | + ip_line | + plain_line) + + # Парсер блока параметров. + param_block = (Group(Optional(action_symbols, default='')('action') + + statement + Optional(statement_name) + + Optional(statement_class))('name') + + block('content') + + Optional(semicolon.suppress())).setParseAction( + self._add_param_block + ) + + # Виды блочных директив. + block_types = (inet_spec | param_block) + + # Парсер параметров с комментариями. + # Note: Применение parser.ignore(comments) является причиной странного + # поведения парсера, при котором невозможно многократное повторное + # применение формата после установки флага ignore_comments. + if self._ignore_comments: + param_line_with_comments = (ZeroOrMore(comments).suppress()( + 'comments' + ) + + param_line('value') + ).setParseAction( + self._add_comments_to_paramline + ) + else: + param_line_with_comments = (ZeroOrMore(comments)('comments') + + param_line('value') + ).setParseAction( + self._add_comments_to_paramline + ) + + # Парсер блока с комментариями. + if self._ignore_comments: + param_block_with_comments = (ZeroOrMore(comments).suppress()( + 'comments' + ) + + block_types('value') + ).setParseAction( + self._add_comments_to_block + ) + else: + param_block_with_comments = (ZeroOrMore(comments)('comments') + + block_types('value') + ).setParseAction( + self._add_comments_to_block + ) + + # Парсер содержимого блоков. + block_item = (param_block_with_comments | + param_line_with_comments) + + # Для парсинга всего блока с любым содержимым. + block << Group(left_brace.suppress() + ZeroOrMore(block_item) + + right_brace.suppress()) + + # Парсер всего документа. + self._document_parser = OneOrMore(block_item) + + def _parse_text(self, text): + parsing_result = self._document_parser.parseString(text, parseAll=True) + list_of_elements = parsing_result.asList() + for part in list_of_elements: + self._join_dictionary(self._document_dictionary, + part) + + def _join_dictionary(self, out_dictionary, dictionary_to_add): + for key in dictionary_to_add: + if dictionary_to_add == OrderedDict(): + return + if key in out_dictionary and\ + isinstance(dictionary_to_add[key], OrderedDict) and\ + isinstance(out_dictionary[key], OrderedDict): + self._join_dictionary(out_dictionary[key], + dictionary_to_add[key]) + else: + out_dictionary[key] = dictionary_to_add[key] + + def _add_plain_line(self, current_parse): + name = tuple(current_parse.name.asList()) + if not current_parse.parameter == '': + value = current_parse.parameter + else: + if current_parse.name.action == '-': + return OrderedDict() + value = '' + + return OrderedDict({name: [value]}) + + def _add_include_line(self, current_parse): + name = current_parse.asList() + return OrderedDict({tuple(name): ['']}) + + def _add_ipline(self, current_parse): + ip_value = current_parse.IP + return OrderedDict({tuple(ip_value): ['']}) + + def _add_inet_specline(self, current_parse): + # Удаляем пробельные символы из второго параметра директивы. + current_parse.name.parameter = current_parse.name.parameter.strip() + block_name = tuple(current_parse.name.asList()) + block_content = current_parse.content.asList() + content = OrderedDict({'#': []}) + for item in block_content: + current_keyword, values = item + current_keyword = tuple(current_keyword) + content[current_keyword] = values + return OrderedDict({block_name: content}) + + def _add_param_block(self, current_parse): + block_name = tuple(current_parse.name.asList()) + block_content = current_parse.content.asList() + content = OrderedDict({'#': []}) + + for item in block_content: + self._join_dictionary(content, item) + return OrderedDict({block_name: content}) + + def _add_comments_to_paramline(self, current_parse): + [parameter] = current_parse.value.asList() + comments = current_parse.comments + if parameter == OrderedDict(): + if not comments == '': + self._last_comments_list.extend(comments.asList()) + return OrderedDict() + + name = next(iter(parameter)) + value = parameter[name] + + if not comments == '': + comments_list = comments.asList() + parameter[name] = (self._last_comments_list + + comments_list + value) + self._last_comments_list = [] + return parameter + + def _add_comments_to_block(self, current_parse): + [value] = current_parse.value + [block_name] = value + block = value[block_name] + + if not current_parse.comments == '': + block['#'] = (self._last_comments_list + + current_parse.comments.asList()) + self._last_comments_list = [] + else: + block.pop('#') + return value + + def _create_comment_list(self, current_parse): + comments_list = [] + comments = current_parse.asList() + for comment in comments: + lines = comment.splitlines() + for line in lines: + comments_list.append(line.strip()) + return comments_list diff --git a/calculate/templates/format/compiz_format.py b/calculate/templates/format/compiz_format.py new file mode 100644 index 0000000..736b8fe --- /dev/null +++ b/calculate/templates/format/compiz_format.py @@ -0,0 +1,150 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import originalTextFor, Literal, ZeroOrMore, Word, printables,\ + OneOrMore, alphanums, ParseException, restOfLine,\ + pyparsing_unicode, Group, Optional + + +class CompizFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_section_line, + self._parse_parameter_line, + self._parse_to_delete_line] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._comments_processing = True + self._need_finish = True + self._format = 'compiz' + + self._current_section = OrderedDict() + self._current_section_name = '' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + section_name = originalTextFor( + OneOrMore(Word(alphanums+'_')) + ) + + action_symbols = (Literal('!') | Literal('-')) + + self._section_line = (Literal('[').suppress() + + Optional(action_symbols, default='')('action') + + section_name('name') + + Literal(']').suppress())('section_name') + + parameter_name = originalTextFor( + OneOrMore(Word(printables, + excludeChars='=')) + ) + + parameter_value = Word(printables) + + self._parameter_line = (Group(Optional(action_symbols, + default='')('action') + + parameter_name('name') + )('parameter_name') + + Literal('=').suppress() + + parameter_value('parameter_value')) + + self._parameter_to_delete = (action_symbols('action') + + parameter_name('name') + + restOfLine.suppress())('parameter_name') + + self._comment_line = originalTextFor( + Literal('#') + + ZeroOrMore(Word(printables + + pyparsing_unicode.alphanums)) + )('comment') + + def _parse_section_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._section_line.parseString(line) + self._match = True + + if self._current_section_name != '': + if self._current_section_name in \ + self._document_dictionary.keys(): + self._document_dictionary[self._current_section_name].\ + update(self._current_section) + self._current_section = OrderedDict() + else: + self._item_to_add[self._current_section_name] = \ + self._current_section + self._current_section = OrderedDict() + self._ready_to_update = True + + self._current_section_name = tuple( + parsing_result.section_name.asList() + ) + + if self._last_comments_list != []: + self._current_section['#'] = self._last_comments_list + self._last_comments_list = [] + except ParseException: + return + + def _parse_parameter_line(self, line): + try: + parsing_result = self._parameter_line.parseString(line) + self._match = True + + key_value = tuple(parsing_result.parameter_name.asList()) + parameter_value = parsing_result.parameter_value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + + self._current_section[key_value] = parameter_value + except ParseException: + return + + def _parse_to_delete_line(self, line): + try: + parsing_result = self._parameter_to_delete.parseString(line) + self._match = True + + if parsing_result.action == '-': + return + + key_value = tuple(parsing_result.parameter_name.asList()) + parameter_value = self._last_comments_list + self._last_comments_list = [] + + self._current_section[key_value] = parameter_value + except ParseException: + return + + def _parse_comment_line(self, line): + try: + parsing_result = self._comment_line.parseString(line) + self._match = True + + if not self._ignore_comments: + self._last_comments_list.append(parsing_result.comment) + except ParseException: + return + + def _finish_method(self): + if self._current_section_name in self._document_dictionary.keys(): + self._document_dictionary[self._current_section_name].update( + self._current_section + ) + else: + self._item_to_add[self._current_section_name] =\ + self._current_section + self._document_dictionary.update(self._item_to_add) + + self._current_section = OrderedDict() diff --git a/calculate/templates/format/diff_format.py b/calculate/templates/format/diff_format.py new file mode 100644 index 0000000..7aa6657 --- /dev/null +++ b/calculate/templates/format/diff_format.py @@ -0,0 +1,62 @@ +# vim: fileencoding=utf-8 +# +from calculate.utils.files import Process +from os import path + + +class DiffFormat(): + def __init__(self, document_text: str): + self._patch_text = document_text + self._root_path = '' + self._last_level = 0 + + # вынести в более общий класс или куда-то еще. + self._changed_files_list = [] + + def execute_format(self, root_path): + if path.exists(root_path): + self._root_path = root_path + else: + # Какая-то обработка ошибки. + error_message = 'Root path does not exist.' + print(error_message) + return False + + if self._patch_text: + return self._patch_document() + else: + # Какая-то обработка ошибки. + error_message = 'Empty patch file.' + print(error_message) + return False + + def _patch_document(self): + for level in range(0, 4): + patch_dry_run = Process('patch', '--dry-run', + '-p{}'.format(level), cwd=self._root_path) + patch_dry_run.write(self._patch_text) + if patch_dry_run.success(): + break + + patch_dry_run = Process('patch', '-R', '--dry-run', + '-p{}'.format(level), cwd=self._root_path) + patch_dry_run.write(self._patch_text) + if patch_dry_run.success(): + return '' + else: + # Какая-то обработка ошибки. + error_message = 'Correction failed.' + print(error_message) + return False + + self._last_level = level + patch_run = Process('patch', '-p{}'.format(level), cwd=self._root_path) + patch_run.write(self._patch_text) + + if patch_run.success(): + for line in patch_run: + if line.startswith('patching file'): + self._changed_files_list.append(line[13:].strip()) + return patch_run.read() + else: + return '' diff --git a/calculate/templates/format/dovecot_format.py b/calculate/templates/format/dovecot_format.py new file mode 100644 index 0000000..f4397ed --- /dev/null +++ b/calculate/templates/format/dovecot_format.py @@ -0,0 +1,229 @@ +# vim: fileencoding=utf-8 +# +# ToDo: написать счетчик скобок для финальной оценки корректности +# документа. +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import originalTextFor, Literal, ZeroOrMore, Word, printables,\ + OneOrMore, alphanums, ParseException, pyparsing_unicode,\ + Group, Optional, alphas, lineEnd, lineStart, Keyword + + +class DovecotFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_section_start_line, + self._parse_include_line, + self._parse_section_end_line, + self._parse_parameter_line, + self._parse_parameter_to_delete_line] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._need_finish = True + self._comments_processing = True + self._format = 'dovecot' + + self._section_stack = OrderedDict() + self._current_section_name = '' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + # Знаки пунктуации и действий. + left_brace = Literal('{') + right_brace = Literal('}') + action_symbols = (Literal('!') | Literal('-')) + + self._comment_line_parser = originalTextFor( + Literal('#') + + ZeroOrMore(Word( + printables + + pyparsing_unicode.alphanums) + ) + )('comment') + + # Для парсинга строк с началом секций. + section = Word(alphas, alphanums+'-_', excludeChars='{}') + + section_name = Word(printables, excludeChars='{}') + + self._section_start_parser = Group(Optional(action_symbols, + default='')('action') + + section + + Optional(section_name) + + left_brace.suppress())('name') + + # Для парсинга строк, указывающих конец секций. + self._section_end_parser = lineStart() + right_brace + lineEnd() + + # Для парсинга строк, содержащих параметры. + parameter_name = Word(alphas, alphanums+'_-', excludeChars='{}=') + + parameter_value = OneOrMore(Word(printables)) + + self._parameter_line_parser = (Group(Optional(action_symbols, + default='')('action') + + parameter_name)('name') + + Literal('=').suppress() + + originalTextFor( + parameter_value + )('value')) + + # Для парсинга строк с параметрами, подлежащими удалению. + self._parameter_to_delete_parser = (action_symbols('action') + + parameter_name + + Optional(Literal('=')).suppress() + ) + + # Для парсинга строк, содержащих директиву !include. + include = Keyword('!include') | Keyword('!include_try') + + include_line_plain = (Optional(~action_symbols, default='')('action') + + include('keyword') + Word(printables)('value')) + + include_line_to_delete = (action_symbols('action') + include('keyword') + + Word(printables)('value')) + + self._include_line_parser = (include_line_plain | + include_line_to_delete) + + def _parse_section_start_line(self, line): + try: + parsing_result = self._section_start_parser.parseString(line) + self._match = True + + section_name = tuple(parsing_result.name.asList()) + + if not self._last_comments_list == []: + section_content = OrderedDict({'#': self._last_comments_list}) + self._last_comments_list = [] + else: + section_content = OrderedDict() + + new_section = OrderedDict({section_name: section_content}) + + if self._section_stack == OrderedDict(): + if section_name in self._document_dictionary: + new_section = OrderedDict( + {section_name: + self._document_dictionary[section_name]} + ) + else: + if section_name in \ + self._section_stack[self._current_section_name]: + new_section = OrderedDict({ + section_name: + self._section_stack[self._current_section_name] + [section_name] + }) + else: + self._section_stack[self._current_section_name].update( + new_section + ) + self._section_stack.update(new_section) + self._current_section_name = section_name + except ParseException: + return + + def _parse_section_end_line(self, line): + try: + self._section_end_parser.parseString(line) + self._match = True + + last_section_name, last_section = self._section_stack.popitem() + + if self._section_stack == OrderedDict(): + self._item_to_add = OrderedDict({last_section_name: + last_section}) + self._ready_to_update = True + self._current_section_name = '' + else: + self._current_section_name = next(reversed( + self._section_stack + )) + except ParseException: + return + + def _parse_parameter_line(self, line): + try: + parsing_result = self._parameter_line_parser.parseString(line) + self._match = True + + parameter_name = tuple(parsing_result.name.asList()) + parameter_value = (self._last_comments_list + + [parsing_result.value.strip()]) + self._last_comments_list = [] + parameter = OrderedDict({parameter_name: parameter_value}) + + if self._section_stack == OrderedDict(): + self._item_to_add = parameter + self._ready_to_update = True + else: + self._section_stack[self._current_section_name].update( + parameter + ) + except ParseException: + return + + def _parse_parameter_to_delete_line(self, line): + try: + parsing_result = self._parameter_to_delete_parser.parseString(line) + self._match = True + + parameter_name = tuple(parsing_result.asList()) + parameter_value = self._last_comments_list + [''] + self._last_comments_list = [] + parameter = OrderedDict({parameter_name: parameter_value}) + + if self._section_stack == OrderedDict(): + self._item_to_add = parameter + self._ready_to_update = True + else: + self._section_stack[self._current_section_name].update( + parameter + ) + except ParseException: + return + + def _parse_include_line(self, line): + try: + parsing_result = self._include_line_parser.parseString(line) + self._match = True + + parameter_name = tuple(parsing_result.asList()) + + if parsing_result.action == '-': + return + + parameter_value = self._last_comments_list + [''] + self._last_comments_list = [] + include_item = OrderedDict({parameter_name: parameter_value}) + + if self._section_stack == OrderedDict(): + self._item_to_add = include_item + self._ready_to_update = True + else: + self._section_stack[self._current_section_name].update( + include_item + ) + except ParseException: + return + + def _parse_comment_line(self, line): + try: + parsing_result = self._comment_line_parser.parseString(line) + self._match = True + + if not self._ignore_comments: + self._last_comments_list.append(parsing_result.comment) + except ParseException: + return diff --git a/calculate/templates/format/json_format.py b/calculate/templates/format/json_format.py new file mode 100644 index 0000000..1571580 --- /dev/null +++ b/calculate/templates/format/json_format.py @@ -0,0 +1,26 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +import json + + +class JSONFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [] + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._format = 'json' + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + self._text_to_dictionary(document_text) + + def _text_to_dictionary(self, json_file_text): + self._document_dictionary = json.loads(json_file_text, + object_pairs_hook=OrderedDict) + + def get_document_text(self): + json_file_text = json.dumps(self._document_dictionary, indent=4) + return json_file_text diff --git a/calculate/templates/format/kde_format.py b/calculate/templates/format/kde_format.py new file mode 100644 index 0000000..34292eb --- /dev/null +++ b/calculate/templates/format/kde_format.py @@ -0,0 +1,155 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import originalTextFor, Literal, ZeroOrMore, Word, printables,\ + OneOrMore, alphanums, ParseException, restOfLine,\ + pyparsing_unicode, Group, Optional + + +class KDEFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_section_line, + self._parse_parameter_line, + self._parse_to_delete_line] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._comments_processing = True + self._need_finish = True + self._format = 'kde' + + self._current_section = OrderedDict() + self._current_section_name = '' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + action_symbols = (Literal('!') | Literal('-')) + section_name_part_content = originalTextFor((OneOrMore( + Word(alphanums+':')))) + + section_name_part = (Literal('[').suppress() + + section_name_part_content + + Literal(']').suppress()) + + self._section_line = (Literal('[').suppress() + + Optional(action_symbols, default='')('action') + + section_name_part_content + + Literal(']').suppress() + + ZeroOrMore(section_name_part))('section_name') + + parameter_name = originalTextFor( + OneOrMore(Word(printables, + excludeChars='=')) + )('parameter_name') + + parameter_value = originalTextFor(OneOrMore(Word( + pyparsing_unicode.alphanums + + printables)) + )('parameter_value') + + self._parameter_line = (Group(Optional(action_symbols, + default='')('action') + + parameter_name)('parameter_name') + + Literal('=').suppress() + + parameter_value('parameter_value')) + + self._parameter_to_delete = (action_symbols('action') + + parameter_name('name') + + restOfLine.suppress())('parameter_name') + + self._comment_line = originalTextFor( + Literal('#') + + ZeroOrMore(Word(printables + + pyparsing_unicode.alphanums)) + )('comment') + + def _parse_section_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._section_line.parseString(line) + self._match = True + + if self._current_section_name != '': + if self._current_section_name in \ + self._document_dictionary.keys(): + self._document_dictionary[self._current_section_name].\ + update(self._current_section) + self._current_section = OrderedDict() + else: + self._item_to_add[self._current_section_name] = \ + self._current_section + self._current_section = OrderedDict() + self._ready_to_update = True + + self._current_section_name = tuple( + parsing_result.section_name.asList() + ) + + if self._last_comments_list != []: + self._current_section['#'] = self._last_comments_list + self._last_comments_list = [] + except ParseException: + return + + def _parse_parameter_line(self, line): + try: + parsing_result = self._parameter_line.parseString(line) + self._match = True + + key_value = tuple(parsing_result.parameter_name.asList()) + parameter_value = parsing_result.parameter_value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + + self._current_section[key_value] = parameter_value + except ParseException: + return + + def _parse_to_delete_line(self, line): + try: + parsing_result = self._parameter_to_delete.parseString(line) + self._match = True + + if parsing_result.action == '-': + return + + key_value = tuple(parsing_result.parameter_name) + parameter_value = self._last_comments_list + self._last_comments_list = [] + + self._current_section[key_value] = parameter_value + except ParseException: + return + + def _parse_comment_line(self, line): + try: + parsing_result = self._comment_line.parseString(line) + self._match = True + + if not self._ignore_comments: + self._last_comments_list.append(parsing_result.comment) + except ParseException: + return + + def _finish_method(self): + if self._current_section_name in self._document_dictionary.keys(): + self._document_dictionary[self._current_section_name].update( + self._current_section + ) + else: + self._item_to_add[self._current_section_name] =\ + self._current_section + self._document_dictionary.update(self._item_to_add) + + self._current_section = OrderedDict() diff --git a/calculate/templates/format/kernel_format.py b/calculate/templates/format/kernel_format.py new file mode 100644 index 0000000..6c75d57 --- /dev/null +++ b/calculate/templates/format/kernel_format.py @@ -0,0 +1,100 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import Word, Literal, alphanums, printables, originalTextFor,\ + ZeroOrMore, OneOrMore, ParseException, restOfLine,\ + pyparsing_unicode, Group, Optional + + +class KernelFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_parameter_line, + self._parse_to_delete_line] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._comments_processing = True + self._format = 'kernel' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + documentLines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(documentLines) + + def _initialize_parser(self): + parameter_name = Word(alphanums+'_')('parameter_name') + + parameter_value = originalTextFor( + OneOrMore(Word(printables)) + )('parameter_value') + + action_symbols = (Literal('!') | Literal('-')) + + self._parameter_line = (Group(Optional(action_symbols, + default='')('action') + + parameter_name('name') + )('parameter_name') + + Literal('=').suppress() + + parameter_value('parameter_value')) + + self._parameter_to_delete = (Group(action_symbols('action') + + parameter_name('name') + )('parameter_name') + + restOfLine.suppress()) + + self._comment_line = originalTextFor( + Literal('#').suppress() + + ZeroOrMore(Word(printables + + pyparsing_unicode.alphanums)) + )('Comment') + + def _parse_parameter_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._parameter_line.parseString(line) + + key_value = tuple(parsing_result.parameter_name.asList()) + parameter_value = parsing_result.parameter_value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + + self._item_to_add[key_value] = parameter_value + self._ready_to_update = True + self._match = True + except ParseException: + return + + def _parse_to_delete_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._parameter_to_delete.parseString(line) + + if parsing_result.action == '-': + return + + key_value = (parsing_result.parameter_name.action, + parsing_result.parameter_name.name.upper()) + parameter_value = self._last_comments_list + self._last_comments_list = [] + + self._item_to_add[key_value] = parameter_value + self._ready_to_update = True + self._match = True + except ParseException: + return + + def _parse_comment_line(self, line): + try: + result = self._comment_line.parseString(line) + self._match = True + if not self._ignore_comments: + self._last_comments_list.append(result.Comment) + except ParseException: + return diff --git a/calculate/templates/format/ldap_format.py b/calculate/templates/format/ldap_format.py new file mode 100644 index 0000000..0568d4a --- /dev/null +++ b/calculate/templates/format/ldap_format.py @@ -0,0 +1,522 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import originalTextFor, Literal, ZeroOrMore, Word, printables,\ + OneOrMore, alphanums, ParseException, restOfLine,\ + pyparsing_unicode, nums, delimitedList, Optional,\ + Keyword, SkipTo, Group + + +class LDAPFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_type_line, + self._parse_access_line, + self._parse_access_line_to_delete, + self._parse_syncrepl_line, + self._parse_syncrepl_line_to_delete, + self._parse_notunique_line, + self._parse_index_line, + self._parse_index_line_to_delete, + self._parse_plain_directive_line, + self._parse_plain_directive_line_to_delete] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._comments_processing = True + self._need_finish = True + self._format = 'ldap' + + if self._ignore_comments: + self._current_type_section = OrderedDict() + else: + self._current_type_section = OrderedDict({'#': []}) + + self._current_type = ('', 'global') + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + self._comment_line = originalTextFor( + Literal('#') + + ZeroOrMore(Word(printables + + pyparsing_unicode.alphanums)) + )('comment') + + action_symbols = (Literal('!') | Literal('-')) + assignment = Literal('=') + + # Для парсинга строк c директивами неуникальными для секции. + not_unique_directives = originalTextFor( + Keyword('include') | + Keyword('moduleload') + ) + + not_unique_value = originalTextFor( + OneOrMore(Word(printables)) + )('value') + + self._not_unique_parser = (Optional(action_symbols, + default='')('action') + + not_unique_directives + + not_unique_value + restOfLine.suppress()) + + # Для выделения областей global, backend и database. + type_sections_keywords = originalTextFor( + Keyword('backend') | + Keyword('database') + ) + + type_value = originalTextFor(Word(alphanums)) + + self._type_line = (Optional(action_symbols, default='')('action') + + type_sections_keywords + + type_value + + restOfLine.suppress()) + + # Для парсинга конструкции syncrepl rid= + content_without_spaces = Word(printables, excludeChars='"') + + content_with_spaces = (Literal('"') + + OneOrMore(Word(printables, + excludeChars='"')) + + Literal('"')) + + parameter_without_spaces = (Word(printables, excludeChars='"=') + + assignment.suppress() + + content_without_spaces) + + parameter_with_spaces = (Word(printables, excludeChars='"=') + + assignment.suppress() + + content_with_spaces) + + values = OneOrMore(originalTextFor(parameter_with_spaces | + parameter_without_spaces))('Values') + + syncrepl_replica_id = originalTextFor(Literal('rid') + + assignment.suppress() + + Word(nums))('replicaID') + + self._syncrepl_line_parser = (Group(Optional(action_symbols, + default='')('action') + + Keyword('syncrepl') + + syncrepl_replica_id)('name') + + values('Values') + + restOfLine.suppress()) + + self._syncrepl_value_parser = (Group(Optional(action_symbols, + default='')('action') + + originalTextFor( + Word( + printables, + excludeChars='"=' + ) + ))('name') + + assignment.suppress() + + originalTextFor( + OneOrMore( + Word(printables) + ) + )('value')) + + self._syncrepl_line_to_delete_parser = (Group(Optional( + action_symbols, + default='' + )('action') + + Keyword('syncrepl') + + syncrepl_replica_id)('name') + + restOfLine.suppress()) + + # Для парсинга конструкции + # access to by || + access_keyword = originalTextFor(Literal('access to'))('keyword') + + value = originalTextFor(parameter_with_spaces | + parameter_without_spaces | + content_without_spaces | + content_with_spaces) + + self._access_line_parser = (Group(Optional(action_symbols, + default='')('action') + + access_keyword + + value)('name') + + Keyword('by').suppress() + + delimitedList( + originalTextFor(value + + SkipTo( + Keyword('by'), + include=False) | + restOfLine + ), + delim='by' + )('Values')) + + self._access_value_parser = (Group(Optional(action_symbols, + default='')('action') + + originalTextFor(value))('name') + + originalTextFor( + Optional(Word(alphanums)) + )('value')) + + self._access_line_to_delete_parser = (Group(action_symbols('action') + + access_keyword + + value + + restOfLine.suppress())('name')) + + # Для парсинга строк с директивами index. + self._index_line_parser = (Group(Optional(action_symbols, + default='')('action') + + Keyword('index') + + originalTextFor(Word(printables)) + )('name') + + originalTextFor( + OneOrMore(Word(printables)) + )('value')) + + self._index_line_to_delete_parser = (Group(action_symbols('action') + + Keyword('index') + + originalTextFor( + Word(printables) + ))('name')) + + # Для парсинга остальных директив. + self._directive_line_parser = (Group(Optional(action_symbols, + default='')('action') + + originalTextFor( + Word(printables) + ))('name') + + originalTextFor( + OneOrMore(Word( + printables + ) + ))('value')) + + self._directive_line_to_delete_parser = (action_symbols('action') + + originalTextFor( + Word(printables) + ))('name') + + def _get_list_of_logic_lines(self, text): + list_of_lines = [] + lines_to_join = [] + for line in text.splitlines(): + if line.strip() == '': + continue + + if not line.startswith(' ') and not line.startswith('\t'): + joined_line = "".join(lines_to_join) + if joined_line != '': + list_of_lines.append(joined_line) + lines_to_join = [] + + line.strip() + else: + line = ' ' + line.strip() + + lines_to_join.append(line) + + joined_line = "".join(lines_to_join) + list_of_lines.append(joined_line) + + return list_of_lines + + def _parse_type_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._type_line.parseString(line) + self._match = True + + type_name = tuple(parsing_result.asList()) + + if self._current_type in self._document_dictionary.keys(): + self._document_dictionary[self._current_type].update( + self._current_type_section + ) + else: + self._item_to_add[self._current_type] =\ + self._current_type_section + self._ready_to_update = True + + # Если глобальная область пуста -- передаем ее комментарии + # следующей за ней области. + if self._current_type == ('', 'global') and\ + list(self._current_type_section.keys()) == ['#']: + self._last_comments_list = self._current_type_section['#'] + self._item_to_add[self._current_type] = OrderedDict() + + self._current_type_section = OrderedDict() + self._current_type = type_name + + if self._last_comments_list != []: + self._current_type_section['#'] = self._last_comments_list + self._last_comments_list = [] + except ParseException: + return + + def _parse_notunique_line(self, line): + '''Метод для парсинга строк c директивами неуникальными для секции. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + self._item_to_add = OrderedDict() + parsing_result = self._not_unique_parser.parseString(line) + self._match = True + + parsing_result.value = parsing_result.value.strip() + + not_unique_name = tuple(parsing_result.asList()) + parameter_value = [''] + + parameter_value = self._last_comments_list + parameter_value + self._last_comments_list = [] + self._current_type_section[not_unique_name] = parameter_value + except ParseException: + return + + def _parse_access_line(self, line): + '''Метод для парсинга строк содержащих конструкцию + access to by ||. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + parsing_result = self._access_line_parser.parseString(line) + self._match = True + + values = [value.strip() for value in + parsing_result.Values.asList()] + values.reverse() + + parameter_name = tuple(parsing_result.name) + + value_dictionary = OrderedDict() + if self._last_comments_list != []: + value_dictionary['#'] = self._last_comments_list + self._last_comments_list = [] + + for value in values: + try: + value_parsing = self._access_value_parser.\ + parseString(value) + + param_name = tuple(value_parsing.name) + param_value = value_parsing.value + value_dictionary[param_name] = [param_value] + except ParseException: + continue + + parameter_value = value_dictionary + + if parameter_name in self._current_type_section.keys(): + self._current_type_section[parameter_name].update( + value_dictionary + ) + else: + self._current_type_section[parameter_name] = parameter_value + except ParseException: + return + + def _parse_access_line_to_delete(self, line): + '''Метод для парсинга строк, предписывающих удаление конструкций + access to, если указано только ее название и значение What. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + parsing_result = self._access_line_to_delete_parser.parseString( + line + ) + self._match = True + + parameter_name = tuple(parsing_result.name.asList()) + + if parsing_result.name.action == '-': + return + + parameter_value = OrderedDict({'#': self._last_comments_list}) + self._last_comments_list = [] + self._current_type_section[parameter_name] = parameter_value + except ParseException: + return + + def _parse_syncrepl_line(self, line): + '''Метод для парсинга строк содержащих конструкцию syncrepl + rep=. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + parsing_result = self._syncrepl_line_parser.parseString(line) + self._match = True + + values = [value.strip() for value in parsing_result.Values.asList()] + parameter_name = tuple(parsing_result.name.asList()) + + value_dictionary = OrderedDict() + if self._last_comments_list != []: + value_dictionary['#'] = self._last_comments_list + self._last_comments_list = [] + + for value in values: + try: + value_parsing = self._syncrepl_value_parser.parseString( + value + ) + param_name = tuple(value_parsing.name.asList()) + param_value = value_parsing.value + value_dictionary[param_name] = [param_value] + except ParseException: + continue + + parameter_value = value_dictionary + + if parameter_name in self._current_type_section.keys(): + self._current_type_section[parameter_name].update( + value_dictionary + ) + else: + self._current_type_section[parameter_name] = parameter_value + except ParseException: + return + + def _parse_syncrepl_line_to_delete(self, line): + '''Метод для парсинга строк, предписывающих удаление конструкций + syncrepl rid=, если указано только ее название и значение + ReplicaID. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + parsing_result = self._syncrepl_line_to_delete_parser.parseString( + line + ) + self._match = True + + parameter_name = tuple(parsing_result.name.asList()) + + if parsing_result.name.action == '-': + return + + parameter_value = OrderedDict({'#': self._last_comments_list}) + self._last_comments_list = [] + self._current_type_section[parameter_name] = parameter_value + except ParseException: + return + + def _parse_index_line(self, line): + '''Метод для парсинга строк с директивами index. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + parsing_result = self._index_line_parser.parseString(line) + self._match = True + + parameter_name = tuple(parsing_result.name.asList()) + + parameter_value = parsing_result.value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + self._current_type_section[parameter_name] = parameter_value + except ParseException: + return + + def _parse_index_line_to_delete(self, line): + '''Метод для парсинга строк, предписывающих удаление директив index, + если указано только из имя, но отсутвует значение. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + parsing_result = self._index_line_to_delete_parser.parseString(line) + self._match = True + + parameter_name = tuple(parsing_result.name.asList()) + + if parsing_result.name.action == '-': + return + + parameter_value = self._last_comments_list + self._last_comments_list = [] + self._current_type_section[parameter_name] = parameter_value + except ParseException: + return + + def _parse_plain_directive_line(self, line): + '''Метод для парсинга строк с простыми уникальными для секции + директивами. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + parsing_result = self._directive_line_parser.parseString(line) + self._match = True + + parameter_name = tuple(parsing_result.name.asList()) + parameter_value = parsing_result.value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + self._current_type_section[parameter_name] = parameter_value + except ParseException: + return + + def _parse_plain_directive_line_to_delete(self, line): + '''Метод для парсинга строк, предписывающих удаление простых уникальных + директив, если указано только их имя, но отсутствует значение. + Аргументы: line -- строка, которую нужно распарсить. + ''' + try: + parsing_result = self._directive_line_to_delete_parser.parseString( + line + ) + self._match = True + + parameter_name = tuple(parsing_result.name.asList()) + + if parsing_result.action == '-': + return + + parameter_value = self._last_comments_list + self._last_comments_list = [] + self._current_type_section[parameter_name] = parameter_value + except ParseException: + return + + def _parse_comment_line(self, line): + '''Метод для парсинга строк с комментариями и добавления их в список + комментариев _last_comments_list, предназначенный для сбора + комментариев и последующего их присваивания параметрам и секциям. + Аргументы: line -- строка, которую нужно распарсить.''' + try: + parsing_result = self._comment_line.parseString(line) + self._match = True + + if not self._ignore_comments: + # До того, как первый элемент встречен -- все комментарии + # должны быть присвоены глобальной области. + if self._current_type == ('', 'global') and\ + list(self._current_type_section.keys()) == ['#']: + self._current_type_section['#'].append( + parsing_result.comment + ) + else: + self._last_comments_list.append(parsing_result.comment) + + except ParseException: + return + + def _finish_method(self): + self._item_to_add = OrderedDict() + if self._current_type in self._document_dictionary.keys(): + self._document_dictionary[self._current_type].update( + self._current_type_section + ) + else: + self._item_to_add[self._current_type] = self._current_type_section + self._document_dictionary.update(self._item_to_add) + + self._item_to_add = OrderedDict() + self._current_type_section = OrderedDict() diff --git a/calculate/templates/format/openrc_format.py b/calculate/templates/format/openrc_format.py new file mode 100644 index 0000000..040243c --- /dev/null +++ b/calculate/templates/format/openrc_format.py @@ -0,0 +1,99 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import Word, Literal, printables, originalTextFor, ZeroOrMore,\ + OneOrMore, ParseException, restOfLine,\ + pyparsing_unicode, Group, Optional + + +class OpenRCFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_parameter_line, + self._parse_to_delete_line] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._comments_processing = True + self._format = 'openrc' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + parameter_name = Word(printables, excludeChars='=') + + parameter_value = originalTextFor(OneOrMore(Word(printables))) + + action_symbols = (Literal('!') | Literal('-')) + + self._parameter_line = (Group(Optional(action_symbols, + default='')('action') + + parameter_name('name'))('parameter_name') + + Literal('=').suppress() + + parameter_value('parameter_value')) + + self._parameter_to_delete = (Group(action_symbols('action') + + parameter_name('name') + )('parameter_name') + + restOfLine.suppress()) + + self._comment_line = originalTextFor( + Literal('#') + + ZeroOrMore(Word(printables + + pyparsing_unicode.alphanums)) + )('comment') + + def _parse_parameter_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._parameter_line.parseString(line) + + key_value = (parsing_result.parameter_name.action, + parsing_result.parameter_name.name.lower()) + parameter_value = parsing_result.parameter_value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + + self._item_to_add[key_value] = parameter_value + self._ready_to_update = True + self._match = True + except ParseException: + return + + def _parse_to_delete_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._parameter_to_delete.parseString(line) + + if parsing_result.parameter_name.action == '-': + return + + key_value = (parsing_result.parameter_name.action, + parsing_result.parameter_name.name.lower()) + parameter_value = self._last_comments_list + self._last_comments_list = [] + + self._item_to_add[key_value] = parameter_value + self._ready_to_update = True + self._match = True + except ParseException: + return + + def _parse_comment_line(self, line): + try: + result = self._comment_line.parseString(line) + self._match = True + + if not self._ignore_comments: + self._last_comments_list.append(result.comment) + except ParseException: + return diff --git a/calculate/templates/format/patch_format.py b/calculate/templates/format/patch_format.py new file mode 100644 index 0000000..5f06785 --- /dev/null +++ b/calculate/templates/format/patch_format.py @@ -0,0 +1,177 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +import re +try: + from lxml.etree.ElementTree import fromstring +except ImportError: + from xml.etree.ElementTree import fromstring + + +class PatchFormat(BaseFormat): + def __init__(self, document_text: str, multiline=False, dotall=False): + processing_methods = OrderedDict() + super().__init__(processing_methods) + self._format = 'patch' + + self._multiline_flag = multiline + self._dotall_flag = dotall + self._parsed_patch = None + + self._document_to_patch = '' + self._FLAG_VALUES = {'True': True, + 'False': False, + 'true': True, + 'false': False, + '1': True, + '0': False} + + self._XML_ROOT_LINE = '\ + {0}' + + if not self._parse_patch(document_text): + # Какая-то обработка ошибки. + print('Error: Can not parse patch document.') + + def _parse_patch(self, patch_text): + xml_patch = self._XML_ROOT_LINE.format(patch_text.strip()) + try: + self._parsed_patch = fromstring(xml_patch) + return True + except Exception: + # Какая-то обработка ошибки. + print('Error: Incorrect text of the template.') + return False + + def execute_format(self, document_to_patch): + if not document_to_patch.strip() == '': + self._document_to_patch = document_to_patch + + if self._parse_patch is None: + return False + else: + if not self._patch_document(document_to_patch): + error_message = 'Error: Can not run patch.' + print(error_message) + return False + + else: + after_patch = self._document_to_patch + self._document_to_patch = '' + return after_patch + + def _patch_document(self, document_to_patch): + patch_iterator = self._parsed_patch.getiterator() + PATCH_DOCUMENT_TAGS = ('reg', 'text') + + patch_element = next(patch_iterator, False) + + if not patch_element or not patch_element.tag == 'patch': + print('Error: Incorrect text of the template.') + return False + + while True: + for patch_tag in PATCH_DOCUMENT_TAGS: + patch_element = next(patch_iterator, None) + + if patch_element is None: + if patch_tag == 'text': + print('Error: Last Text ' + 'object is missed.') + return False + else: + break + + if patch_element.tag == patch_tag: + if patch_element.text is not None: + element_text = patch_element.text.strip() + if element_text == '': + error_message = 'Error: Incorrect text of the \ + template: <{0}>%s'.format( + patch_tag + ) + print(error_message) + return False + else: + error_message = 'Error: Incorrect text of the \ + template: <{0}>'.format( + patch_tag + ) + print(error_message) + return False + + if patch_tag == 'reg': + dotall = patch_element.attrib.get('dotall', False) + regex_flags = 0 + + if 'multiline' in patch_element.attrib: + multiline = patch_element.attrib['multiline'] + if multiline not in self._FLAG_VALUES: + error_message = ('Error: Invalid multiline ' + 'value.') + print(error_message) + return False + else: + multiline = self._FLAG_VALUES[multiline] + + # Если глобально флаг MULTILINE включен, но в + # атрибутах тэга этот флаг присутствует со + # значением False -- для этого регулярного + # выражения этот флаг также будет False. + multiline_global = self._multiline_flag & multiline + else: + multiline = False + multiline_global = self._multiline_flag + + if multiline_global or multiline: + regex_flags |= re.MULTILINE + + if 'dotall' in patch_element.attrib: + dotall = patch_element.attrib['dotall'] + if dotall not in self._FLAG_VALUES: + error_message = 'Error: Invalid dotall value.' + print(error_message) + return False + else: + dotall = self._FLAG_VALUES[dotall] + + # Если глобально флаг DOTALL включен, но в + # атрибутах тэга этот флаг присутствует со + # значением False -- для этого регулярного + # выражения этот флаг также будет False. + dotall_global = self._dotall_flag & dotall + else: + dotall = False + dotall_global = self._dotall_flag + + if dotall_global or dotall: + regex_flags |= re.DOTALL + + regex_expression = re.compile(element_text, + regex_flags) + else: + text_for_replace = element_text + else: + if patch_element.tag in PATCH_DOCUMENT_TAGS: + error_message = 'Error: <{0}> is expected, \ + <{1}> instead.'.format( + patch_tag, + patch_element.tag + ) + print(error_message) + else: + error_message = 'Error: Unknown tag: {0}'.format( + patch_element.tag + ) + print(error_message) + # Какая-то обработка ошибки. + error_message = 'Error: Incorrect text of the template.' + print(error_message) + return False + else: + self._document_to_patch = re.sub(regex_expression, + text_for_replace, + self._document_to_patch) + continue + return True diff --git a/calculate/templates/format/postfix_format.py b/calculate/templates/format/postfix_format.py new file mode 100644 index 0000000..758a20f --- /dev/null +++ b/calculate/templates/format/postfix_format.py @@ -0,0 +1,98 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import Word, Literal, alphanums, printables, originalTextFor,\ + ZeroOrMore, OneOrMore, ParseException,\ + pyparsing_unicode, Group, Optional + + +class PostfixFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_parameter_line, + self._parse_to_delete_line] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._comments_processing = True + self._format = 'postfix' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + parameter_name = Word(alphanums+'_') + + parameter_value = originalTextFor(OneOrMore(Word(printables))) + + action_symbols = (Literal('!') | Literal('-')) + + assignment = Literal('=') + + self._parameter_line = (Group(Optional(action_symbols, + default='')('action') + + parameter_name)('parameter_name') + + assignment.suppress() + + parameter_value('parameter_value')) + + self._parameter_to_delete = (action_symbols('action') + + parameter_name('Name') + )('parameter_name') + + self._comment_line = originalTextFor( + Literal('#') + + ZeroOrMore(Word(printables + + pyparsing_unicode.alphanums)) + )('comment') + + def _parse_parameter_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._parameter_line.parseString(line) + self._match = True + + key_value = tuple(parsing_result.parameter_name.asList()) + parameter_value = parsing_result.parameter_value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + + self._item_to_add[key_value] = parameter_value + self._ready_to_update = True + except ParseException: + return + + def _parse_to_delete_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._parameter_to_delete.parseString(line) + self._match = True + + if parsing_result.action == '-': + return + + key_value = tuple(parsing_result.parameter_name.asList()) + parameter_value = self._last_comments_list + self._last_comments_list = [] + + self._item_to_add[key_value] = parameter_value + self._ready_to_update = True + except ParseException: + return + + def _parse_comment_line(self, line): + try: + parsing_result = self._comment_line.parseString(line) + self._match = True + + if not self._ignore_comments: + self._last_comments_list.append(parsing_result.comment) + except ParseException: + return diff --git a/calculate/templates/format/procmail_format.py b/calculate/templates/format/procmail_format.py new file mode 100644 index 0000000..873c6f5 --- /dev/null +++ b/calculate/templates/format/procmail_format.py @@ -0,0 +1,99 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import Word, Literal, alphanums, printables, originalTextFor,\ + ZeroOrMore, OneOrMore, ParseException, restOfLine,\ + pyparsing_unicode, Group, Optional + + +class ProcmailFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_parameter_line, + self._parse_to_delete_line] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._comments_processing = True + self._format = 'procmail' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + parameter_name = Word(alphanums+'_.') + + parameter_value = originalTextFor(OneOrMore(Word(printables))) + + action_symbols = (Literal('!') | Literal('-')) + + self._parameter_line = (Group(Optional(action_symbols, + default='')('action') + + parameter_name('Name') + )('parameter_name') + + Literal('=').suppress() + + parameter_value('parameter_value')) + + self._parameter_to_delete = (Group(action_symbols('action') + + parameter_name('Name') + )('parameter_name') + + restOfLine.suppress()) + + self._comment_line = originalTextFor( + Literal('#') + + ZeroOrMore( + Word(printables + + pyparsing_unicode.alphanums) + ) + )('comment') + + def _parse_parameter_line(self, line): + try: + parsing_result = self._parameter_line.parseString(line) + self._match = True + + key_value = tuple(parsing_result.parameter_name.asList()) + parameter_value = parsing_result.parameter_value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + + self._item_to_add = OrderedDict({key_value: parameter_value}) + self._ready_to_update = True + except ParseException: + return + + def _parse_to_delete_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._parameter_to_delete.parseString(line) + self._match = True + + if parsing_result.parameter_name.action == '-': + return + + key_value = tuple(parsing_result.parameter_name.asList()) + parameter_value = self._last_comments_list + self._last_comments_list = [] + + self._item_to_add = OrderedDict({key_value: parameter_value}) + self._ready_to_update = True + except ParseException: + return + + def _parse_comment_line(self, line): + try: + parsing_result = self._comment_line.parseString(line) + self._match = True + + if not self._ignore_comments: + self._last_comments_list.append(parsing_result.comment) + except ParseException: + return diff --git a/calculate/templates/format/proftpd_format.py b/calculate/templates/format/proftpd_format.py new file mode 100644 index 0000000..029bdc8 --- /dev/null +++ b/calculate/templates/format/proftpd_format.py @@ -0,0 +1,411 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from jinja2 import Environment, PackageLoader +from collections import OrderedDict +from pyparsing import originalTextFor, Literal, ZeroOrMore, Word, printables,\ + OneOrMore, alphanums, ParseException, pyparsing_unicode,\ + Group, Optional, alphas, Keyword + + +class ProFTPDFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = [self._parse_comment_line, + self._parse_section_start_line, + self._parse_section_end_line, + self._parse_single_key_directive_line, + self._parse_double_key_directive_line, + self._parse_to_delete_double_key_directive_line, + self._parse_full_key_directive_line, + self._parse_plain_directive_line, + self._parse_to_delete_plain_directive_line] + + super().__init__(processing_methods) + self._ignore_comments = ignore_comments + self._need_finish = True + self._comments_processing = True + self._format = 'proftpd' + + self._section_stack = [] + self._actions_stack = [] + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + left_angle_bracket = Literal('<') + right_angle_bracket = Literal('>') + slash = Literal('/') + + action_symbols = (Literal('!') | + Literal('-')) + + directive = Word(alphas, alphanums) + + section_value = Word(printables, excludeChars='<>') + + directive_value = Word(printables) + + self._section_start_parser = (left_angle_bracket.suppress() + + Optional(action_symbols, + default='')('action') + + Group(directive('name') + + originalTextFor( + OneOrMore(section_value) + )('value') + )('directive') + + right_angle_bracket.suppress()) + + self._section_end_parser = (left_angle_bracket.suppress() + + slash.suppress() + + directive('directive') + + right_angle_bracket.suppress()) + + self._plain_directive_parser = (Optional(action_symbols)('action') + + Group(directive('name') + + originalTextFor( + OneOrMore(directive_value) + )('value') + )('directive') + ) + + self._delete_plain_directive_parser = (action_symbols('action') + + directive('directive')) + + single_key_directive = (Keyword('AllowAll') | + Keyword('DenyAll') | + Keyword('AccessDenyMsg') | + Keyword('AccessGrantMsg') | + Keyword('ByteRatioErrMsg') | + Keyword('LeechRatioMsg') | + Keyword('CwdRatioMsg') | + Keyword('FileRatioErrMsg')) + + self._single_key_directive_parser = (Optional(action_symbols)('action') + + single_key_directive( + 'directive' + )) + + double_key_directive = (Keyword('AccessDenyMsg') | + Keyword('AccessGrantMsg') | + Keyword('ByteRatioErrMsg') | + Keyword('Allow from') | Keyword('Allow') | + Keyword('AllowFilter') | + Keyword('AnonymousGroup') | + Keyword('AuthPAMConfig') | + Keyword('Bind') | + Keyword('CDPath') | + Keyword('Define') | + Keyword('Deny from') | Keyword('Deny') | + Keyword('DenyFilter') | + Keyword('DisplayChdir') | + Keyword('ExtendedLog') | + Keyword('AnonRatio') | + Keyword('GroupRatio') | + Keyword('HideGroup') | + Keyword('HideUser') | + Keyword('HostRatio') | + Keyword('Include') | + Keyword('LDAPAttr') | + Keyword('LeechRatioMsg') | + Keyword('CwdRatioMsg') | + Keyword('FileRatioErrMsg') | + Keyword('LogFormat') | + Keyword('MaxClientsPerClass') | + Keyword('PIDFile') | + Keyword('RewriteMap') | + Keyword('RewriteRule') | + Keyword('SetEnv') | + Keyword('SQLConnectInfo') | + Keyword('SQLGroupWhereClause') | + Keyword('SQLLog') | + Keyword('SQLNamedQuery') | + Keyword('SQLShowInfo') | + Keyword('SQLUserInfo') | + Keyword('SQLUserWhereClause') | + Keyword('LoadModule') | + Keyword('LoadFile') | + Keyword('TransferRate') | + Keyword('UnsetEnv') | + Keyword('UserPassword') | + Keyword('UserRatio') | + Keyword('ModuleControlsACLs') | + Keyword('ControlsACLs')) + + self._double_key_directive_parser = (Optional(action_symbols)('action') + + Group((double_key_directive + + directive_value + )('name') + + originalTextFor( + ZeroOrMore( + directive_value + ) + )('value') + )('directive') + ) + + self._delete_double_key_directive_parser = (action_symbols('action') + + Group( + ( + double_key_directive + + directive_value + )('name') + )('directive') + ) + + full_key_directive = (Keyword('AllowClass') | + Keyword('AllowGroup') | + Keyword('AllowUser') | + Keyword('Class') | + Keyword('DenyClass') | + Keyword('DenyGroup') | + Keyword('DenyUser') | + Keyword('DirFakeGroup') | + Keyword('DirFakeUser') | + Keyword('HideFiles') | + Keyword('MaxRetrieveFileSize') | + Keyword('MaxStoreFileSize') | + Keyword('RewriteCondition') | + Keyword('RewriteLock') | + Keyword('TimeoutSession') | + Keyword('UserAlias')) + + self._full_key_directive_parser = (Optional(action_symbols)('action') + + Group(full_key_directive + + OneOrMore( + directive_value + ) + )('directive') + ) + + self._comment_line = originalTextFor( + Literal('#') + + ZeroOrMore(Word(printables + + pyparsing_unicode.alphanums)) + )('comment') + + def _parse_section_start_line(self, line): + try: + parsing_result = self._section_start_parser.parseString(line) + self._match = True + + section_name = tuple(parsing_result.directive.asList()) + self._actions_stack.append(parsing_result.action) + self._section_stack.append(section_name) + + except ParseException: + return + + def _parse_section_end_line(self, line): + try: + parsing_result = self._section_end_parser.parseString(line) + self._match = True + + current_section = self._section_stack.pop() + self._actions_stack.pop() + directive = tuple(parsing_result.directive) + + if not current_section[1] != directive: + # Здесь будет кидаться исключение. + self._fatal_error_flag = True + return + + except ParseException: + return + + def _parse_plain_directive_line(self, line): + try: + parsing_result = self._plain_directive_parser.parseString(line) + self._match = True + + for action_item in self._actions_stack: + if not action_item == '': + action = (action_item, ) + break + else: + action = (parsing_result.action, ) + + if not self._section_stack == []: + context = (tuple(self._section_stack), ) + else: + context = ('', ) + + directive = (parsing_result.directive.name, ) + directive_value = [parsing_result.directive.value] + + directive_name = action + context + directive + directive_value = self._last_comments_list + directive_value + self._last_comments_list = [] + + self._item_to_add = OrderedDict({directive_name: directive_value}) + self._ready_to_update = True + except ParseException: + return + + def _parse_to_delete_plain_directive_line(self, line): + try: + parsing_result = self._delete_plain_directive_parser.parseString( + line + ) + self._match = True + + action = (parsing_result.action, ) + + if not self._section_stack == []: + context = (tuple(self._section_stack), ) + else: + context = ('', ) + + directive = (parsing_result.directive, ) + directive_name = action + context + directive + + directive_value = self._last_comments_list + [''] + self._last_comments_list = [] + + self._item_to_add = OrderedDict({directive_name: directive_value}) + self._ready_to_update = True + except ParseException: + return + + def _parse_single_key_directive_line(self, line): + try: + parsing_result = self._single_key_directive_parser.parseString( + line + ) + self._match = True + + for action_item in self._actions_stack: + if not action_item == '': + action = (action_item, ) + break + else: + action = (parsing_result.action, ) + + if not self._section_stack == []: + context = (tuple(self._section_stack), ) + else: + context = ('', ) + + directive = (parsing_result.directive, ) + directive_value = [''] + + directive_name = action + context + directive + directive_value = self._last_comments_list + directive_value + self._last_comments_list = [] + + self._item_to_add = OrderedDict({directive_name: directive_value}) + self._ready_to_update = True + except ParseException: + return + + def _parse_double_key_directive_line(self, line): + try: + parsing_result = self._double_key_directive_parser.parseString( + line + ) + self._match = True + + for action_item in self._actions_stack: + if not action_item == '': + action = (action_item, ) + break + else: + action = (parsing_result.action, ) + + if not self._section_stack == []: + context = (tuple(self._section_stack), ) + else: + context = ('', ) + + directive = tuple(parsing_result.directive.name.asList()) + directive_value = [parsing_result.directive.value] + + directive_name = action + context + directive + directive_value = self._last_comments_list + directive_value + self._last_comments_list = [] + + self._item_to_add = OrderedDict({directive_name: directive_value}) + self._ready_to_update = True + except ParseException: + return + + def _parse_to_delete_double_key_directive_line(self, line): + try: + parsing_result = self._delete_double_key_directive_parser.\ + parseString(line) + self._match = True + + action = (parsing_result.action, ) + + if not self._section_stack == []: + context = (tuple(self._section_stack), ) + else: + context = ('', ) + + directive = tuple(parsing_result.directive.name.asList()) + directive_name = action + context + directive + + directive_value = self._last_comments_list + [''] + self._last_comments_list = [] + + self._item_to_add = OrderedDict({directive_name: directive_value}) + self._ready_to_update = True + except ParseException: + return + + def _parse_full_key_directive_line(self, line): + try: + parsing_result = self._full_key_directive_parser.parseString(line) + self._match = True + + for action_item in self._actions_stack: + if not action_item == '': + action = (action_item, ) + break + else: + action = (parsing_result.action, ) + + if not self._section_stack == []: + context = (tuple(self._section_stack), ) + else: + context = ('', ) + + directive = tuple(parsing_result.directive.asList()) + directive_value = [''] + + directive_name = action + context + directive + directive_value = self._last_comments_list + directive_value + self._last_comments_list = [] + + self._item_to_add = OrderedDict({directive_name: directive_value}) + self._ready_to_update = True + except ParseException: + return + + def _parse_comment_line(self, line): + try: + result = self._comment_line.parseString(line) + self._match = True + + if not self._ignore_comments: + self._last_comments_list.append(result.comment) + except ParseException: + return + + def get_document_text(self): + file_loader = PackageLoader('calculate.templates.format', + self.TEMPLATES_DIRECTORY) + formats_environment = Environment(loader=file_loader) + formats_environment.globals.update(zip=zip) + formats_environment.add_extension('jinja2.ext.do') + template = formats_environment.get_template(self._format) + document_text = template.render( + document_dictionary=self._document_dictionary + ) + return document_text diff --git a/calculate/templates/format/samba_format.py b/calculate/templates/format/samba_format.py new file mode 100644 index 0000000..ed1e2dd --- /dev/null +++ b/calculate/templates/format/samba_format.py @@ -0,0 +1,163 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from collections import OrderedDict +from pyparsing import originalTextFor, Literal, ZeroOrMore, Word, printables,\ + OneOrMore, alphanums, ParseException, pyparsing_unicode,\ + Optional, Group + + +class SambaFormat(BaseFormat): + def __init__(self, document_text: str, + ignore_comments=False, + join_before=False): + processing_methods = [self._parse_comment_line, + self._parse_section_line, + self._parse_parameter_line, + self._parse_to_delete_line] + + super().__init__(processing_methods) + + self._current_section = OrderedDict() + self._current_section_name = '' + + self._ignore_comments = ignore_comments + self._need_finish = True + self._comments_processing = True + self._join_before = join_before + + self._format = 'samba' + + self._last_comments_list = [] + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + document_lines = self._get_list_of_logic_lines(document_text) + self._lines_to_dictionary(document_lines) + + def _initialize_parser(self): + action_symbols = (Literal('!') | Literal('-')) + + comment_symbols = (Literal('#') | Literal(';')) + + assignment_symbol = Literal('=') + + section_name = originalTextFor(OneOrMore(Word(alphanums+'_'))) + + self._section_line = (Literal('[').suppress() + + Optional(action_symbols, default='')('action') + + section_name('name') + Literal(']').suppress() + )('section_name') + + parameter_name = originalTextFor( + OneOrMore(Word(printables, + excludeChars='=')) + ) + + parameter_value = originalTextFor( + OneOrMore(Word(printables)) + ) + + self._parameter_line = (Group(Optional(action_symbols('action'), + default='') + + parameter_name('name') + )('parameter_name') + + assignment_symbol.suppress() + + parameter_value('parameter_value')) + + self._parameter_to_delete = (action_symbols('action') + + parameter_name('name') + )('parameter_name') + + self._comment_line = originalTextFor( + comment_symbols + + ZeroOrMore(Word( + printables + + pyparsing_unicode.alphanums)) + )('comment') + + def _parse_section_line(self, line): + try: + self._item_to_add = OrderedDict() + parsing_result = self._section_line.parseString(line) + self._match = True + + if self._current_section_name != '': + if self._current_section_name in \ + self._document_dictionary.keys(): + self._document_dictionary[self._current_section_name].\ + update(self._current_section) + else: + self._item_to_add[self._current_section_name] = \ + self._current_section + self._ready_to_update = True + + self._current_section = OrderedDict() + + self._current_section_name = (parsing_result.action, + parsing_result.name.lower()) + + if self._last_comments_list != []: + self._current_section['#'] = self._last_comments_list + self._last_comments_list = [] + except ParseException: + return + + def _parse_parameter_line(self, line): + try: + parsing_result = self._parameter_line.parseString(line) + self._match = True + + key_value = (parsing_result.parameter_name.action, + parsing_result.parameter_name.name.lower()) + parameter_value = parsing_result.parameter_value + + parameter_value = self._last_comments_list + [parameter_value] + self._last_comments_list = [] + + self._current_section[key_value] = parameter_value + except ParseException: + return + + def _parse_to_delete_line(self, line): + try: + parsing_result = self._parameter_to_delete.parseString(line) + self._match = True + + if parsing_result.action == '-': + return + + key_value = (parsing_result.action, + parsing_result.name.lower()) + parameter_value = self._last_comments_list + self._last_comments_list = [] + + self._current_section[key_value] = parameter_value + except ParseException: + return + + def _parse_comment_line(self, line): + try: + parsing_result = self._comment_line.parseString(line) + self._match = True + + if not self._ignore_comments: + self._last_comments_list.append(parsing_result.comment) + except ParseException: + return + + def _finish_method(self): + self._item_to_add = OrderedDict() + if self._current_section_name in self._document_dictionary.keys(): + self._document_dictionary[self._current_section_name].update( + self._current_section + ) + else: + self._item_to_add[self._current_section_name] =\ + self._current_section + self._document_dictionary.update(self._item_to_add) + + self._item_to_add = OrderedDict() + self._current_section = OrderedDict() diff --git a/calculate/templates/format/templates/bind b/calculate/templates/format/templates/bind new file mode 100644 index 0000000..801f466 --- /dev/null +++ b/calculate/templates/format/templates/bind @@ -0,0 +1,27 @@ +{% for item_name, item_value in document_dictionary.items() recursive %} +{% if item_name != '#' %} +{% if item_value is mapping %} +{% if not loop.first %} + +{% endif %} +{% if '#' in item_value %} +{% for comment in item_value['#'] %} +{{ comment }} +{% endfor %} +{% endif %} +{% if item_name[1] == 'inet' %} +{{ item_name[1:] | join(' ') }} +{%- for keyword in item_value %} +{%- if keyword != '#' %} {{ keyword[1] }} { {{ item_value[keyword] | join('; ')}}; }{% endif %}{% endfor %}; +{% else %} +{{ item_name[1:] | join(' ') }} { + {{ loop(item_value.items()) | indent}}}; +{% endif %} +{% else %} +{% for comment in item_value[:-1] %} +{{ comment }} +{% endfor %} +{{ item_name[1:] | join(' ') }}{% if not item_value[-1] == '' %} {{ item_value[-1] }}{% endif%}; +{% endif %} +{% endif %} +{% endfor %} diff --git a/calculate/templates/format/templates/compiz b/calculate/templates/format/templates/compiz new file mode 100644 index 0000000..813caed --- /dev/null +++ b/calculate/templates/format/templates/compiz @@ -0,0 +1,17 @@ +{% for section_name in document_dictionary.keys() %} +{% if '#' in document_dictionary[section_name].keys() %} +{% for comment in document_dictionary[section_name]['#'] %} +{{ comment }} +{% endfor %} +{% endif %} +[{{ section_name[1] }}] +{% for parameter_name in document_dictionary[section_name].keys() %} +{% if parameter_name != '#' %} +{% for comment in document_dictionary[section_name][parameter_name][:-1]%} +{{ comment }} +{% endfor %} +{{ parameter_name[1] }}={{ document_dictionary[section_name][parameter_name][-1] }} +{% endif %} +{% endfor %} + +{% endfor %} diff --git a/calculate/templates/format/templates/dovecot b/calculate/templates/format/templates/dovecot new file mode 100644 index 0000000..d8656ff --- /dev/null +++ b/calculate/templates/format/templates/dovecot @@ -0,0 +1,28 @@ +{% for item_name, item_value in document_dictionary.items() recursive %} +{% if item_name != '#' %} +{% if item_value is mapping %} +{% if '#' in item_value %} +{% for comment in item_value['#'] %} +{{ comment }} +{% endfor %} +{% endif %} +{{ item_name[1:] | join(' ') }} { + {{ loop(item_value.items()) | indent}}} +{% else %} +{% for comment in item_value[:-1] %} +{{ comment }} +{% endfor %} +{{ item_name[1:] | join(' ') }}{%- if not item_value[-1] == '' %} = {{ item_value[-1] }} +{% else %} +{%- if not loop.last%} + +{% endif %} +{% endif %} +{% endif %} +{% if not loop.last%} + +{% endif %} +{% endif %} +{% endfor %} + + diff --git a/calculate/templates/format/templates/kde b/calculate/templates/format/templates/kde new file mode 100644 index 0000000..bcace00 --- /dev/null +++ b/calculate/templates/format/templates/kde @@ -0,0 +1,19 @@ +{% for section_name in document_dictionary.keys() %} +{% if '#' in document_dictionary[section_name].keys() %} +{% for comment in document_dictionary[section_name]['#'] %} +{{ comment }} +{% endfor %} +{% endif %} +{% for section_part in section_name[1:] %} +[{{ section_part }}]{% endfor %} + +{% for parameter_name in document_dictionary[section_name].keys() %} +{% if parameter_name != '#' %} +{% for comment in document_dictionary[section_name][parameter_name][:-1]%} +{{ comment }} +{% endfor %} +{{ parameter_name[1] }}={{ document_dictionary[section_name][parameter_name][-1] }} +{% endif %} +{% endfor %} + +{% endfor %} diff --git a/calculate/templates/format/templates/kernel b/calculate/templates/format/templates/kernel new file mode 100644 index 0000000..e89f330 --- /dev/null +++ b/calculate/templates/format/templates/kernel @@ -0,0 +1,6 @@ +{% for parameter_name in document_dictionary.keys() %} +{% for comment in document_dictionary[parameter_name][:-1]%} +{{ comment }} +{% endfor %} +{{ parameter_name[1] }}={{ document_dictionary[parameter_name][-1] }} +{% endfor %} diff --git a/calculate/templates/format/templates/ldap b/calculate/templates/format/templates/ldap new file mode 100644 index 0000000..025ed01 --- /dev/null +++ b/calculate/templates/format/templates/ldap @@ -0,0 +1,59 @@ +{% for section_type in document_dictionary.keys() %} +{% if '#' in document_dictionary[section_type] %} +{% for comment in document_dictionary[section_type]['#']%} +{{ comment }} +{% endfor %} +{% endif %} +{% if section_type != ('', 'global')%} +{{ section_type[1] }} {{ section_type[2] }} +{% endif %} +{% for directive in document_dictionary[section_type].keys() if directive[1] == 'include' %} +{% for comment in document_dictionary[section_type][directive][:-1] %} +{{ comment }} +{% endfor %} +{{ directive[1] }} {{ directive[2] }} +{% endfor %} +{% for directive in document_dictionary[section_type].keys() %} +{% if directive != '#' and directive[1] != 'include' %} +{% set directive_value = document_dictionary[section_type][directive] %} +{% if directive[1] == 'access to' %} +{% set access_values = directive_value.keys()|list %} +{% if '#' in directive_value %} +{% for comment in directive_value['#'] %} +{{ comment }} +{% endfor %} +{% set access_values = access_values[1:] %} +{% endif %} +{% if access_values|length == 1 %} +{{ directive[1] }} {{ directive[2] }} by {{ access_values[0][1] }} {{ directive_value[access_values[0]][0] }} +{% else %} +{{ directive[1] }} {{ directive[2] }} + {% for value in access_values|reverse %} + by {{ value[1] }} {{ directive_value[value][0] }} + {% endfor %} +{% endif %} +{% elif directive[1] == 'syncrepl' %} +{% set syncrepl_values = directive_value.keys()|list%} +{% if '#' in directive_value %} +{% for comment in directive_value['#'] %} +{{ comment }} +{% endfor %} +{% set syncrepl_values = syncrepl_values[1:]%} +{% endif %} +{{ directive[1] }} {{ directive[2] }} + {% for value in syncrepl_values %} + {{ value[1] }}={{ directive_value[value][0] }} + {% endfor %} +{% else %} +{% for comment in directive_value[:-1] %} +{{ comment }} +{% endfor %} +{{ directive[1:]|join(' ') }}{% if directive_value[-1] != '' %} {{ directive_value[-1] }} +{% else %} + +{% endif %} +{% endif %} +{% endif %} +{% endfor %} + +{% endfor %} diff --git a/calculate/templates/format/templates/openrc b/calculate/templates/format/templates/openrc new file mode 100644 index 0000000..adcf863 --- /dev/null +++ b/calculate/templates/format/templates/openrc @@ -0,0 +1,7 @@ +{% for parameter_name in document_dictionary.keys() %} +{% for comment in document_dictionary[parameter_name][:-1]%} +{{ comment }} +{% endfor %} +{{ parameter_name[1] }}={{ document_dictionary[parameter_name][-1] }} + +{% endfor %} diff --git a/calculate/templates/format/templates/postfix b/calculate/templates/format/templates/postfix new file mode 100644 index 0000000..83944e7 --- /dev/null +++ b/calculate/templates/format/templates/postfix @@ -0,0 +1,8 @@ +{% for parameter_name in document_dictionary.keys() %} +{% for comment in document_dictionary[parameter_name][:-1]%} +{{ comment }} +{% endfor %} +{{ parameter_name[1] }} = {{ document_dictionary[parameter_name][-1] }} + +{% endfor %} + diff --git a/calculate/templates/format/templates/procmail b/calculate/templates/format/templates/procmail new file mode 100644 index 0000000..adcf863 --- /dev/null +++ b/calculate/templates/format/templates/procmail @@ -0,0 +1,7 @@ +{% for parameter_name in document_dictionary.keys() %} +{% for comment in document_dictionary[parameter_name][:-1]%} +{{ comment }} +{% endfor %} +{{ parameter_name[1] }}={{ document_dictionary[parameter_name][-1] }} + +{% endfor %} diff --git a/calculate/templates/format/templates/proftpd b/calculate/templates/format/templates/proftpd new file mode 100644 index 0000000..d85347d --- /dev/null +++ b/calculate/templates/format/templates/proftpd @@ -0,0 +1,38 @@ +{%- set section_stack = [] -%} +{%- set counters = namespace(level=0,indent=0) -%} +{%- for item_name, item_value in document_dictionary.items() -%} +{%- set count_flag = True -%} +{%- set counters.level = 0 -%} +{%- for item_level, current_level in zip(item_name[1], section_stack) -%} +{%- if not item_level == current_level -%} +{%- set count_flag = False -%} +{%- elif count_flag -%} +{%- set counters.level = counters.level + 1 -%} +{%- endif -%} +{%- endfor -%} +{%- for section_name in section_stack[counters.level: ] | reverse -%} +{%- set counters.indent = counters.indent - 1 -%} +{{- ' ' * counters.indent }} +{% if loop.last %} +{% endif -%} +{% do section_stack.pop() -%} +{%- endfor -%} +{%- for comment in item_value[:-1] -%} +{% if loop.first %} +{% endif -%} +{{ ' ' * counters.indent }}{{ comment }} +{% endfor -%} +{%- for section_name in item_name[1][counters.level: ] -%} +{{ ' ' * counters.indent }}<{{ section_name | join(' ') }}> +{% set counters.indent = counters.indent + 1 -%} +{%- do section_stack.append(section_name) -%} +{%- endfor -%} +{{ ' ' * counters.indent }}{{ item_name[2:] | join(' ') }}{% if not item_value[-1] == '' %} {{ item_value[-1] }}{% endif %} +{% endfor -%} +{%- for section_name in section_stack | reverse -%} +{%- set counters.indent = counters.indent - 1 -%} +{{ ' ' * counters.indent }} +{%- if not loop.last %} +{% endif -%} +{%- endfor %} + diff --git a/calculate/templates/format/templates/samba b/calculate/templates/format/templates/samba new file mode 100644 index 0000000..b8cb582 --- /dev/null +++ b/calculate/templates/format/templates/samba @@ -0,0 +1,16 @@ +{% for section_name in document_dictionary.keys() %} +{% if '#' in document_dictionary[section_name].keys() %} +{% for comment in document_dictionary[section_name]['#'] %} +{{ comment }} +{% endfor %} +{% endif %} +[{{ section_name[1] }}] +{% for parameter_name in document_dictionary[section_name].keys() %} +{% if parameter_name != '#' %} + {% for comment in document_dictionary[section_name][parameter_name][:-1]%} + {{ comment }} + {% endfor %} + {{ parameter_name[1] }} = {{ document_dictionary[section_name][parameter_name][-1] }} + {% endif %} + {% endfor %} +{% endfor %} diff --git a/calculate/templates/format/templates/squid b/calculate/templates/format/templates/squid new file mode 100644 index 0000000..7b991bc --- /dev/null +++ b/calculate/templates/format/templates/squid @@ -0,0 +1,14 @@ +{% for parameter_name, parameter_value in document_dictionary.items() %} +{% for comment in document_dictionary[parameter_name][:-1]%} +{{ comment }} +{% endfor %} +{{ parameter_name[1:] | join(' ') }}{%- if not parameter_value[-1] == '' %} {{ parameter_value[-1] }} +{% else %} +{%- if not loop.last%} + + +{% endif %} +{% endif %} +{% endfor %} + + diff --git a/calculate/templates/format/xml_gconf_format.py b/calculate/templates/format/xml_gconf_format.py new file mode 100644 index 0000000..2a1d864 --- /dev/null +++ b/calculate/templates/format/xml_gconf_format.py @@ -0,0 +1,223 @@ +# vim: fileencoding=utf-8 +# +from pyparsing import originalTextFor, Literal, Word, printables, OneOrMore,\ + Optional +from .base_format import BaseFormat +from collections import OrderedDict +try: + from lxml.etree import Element, SubElement, ElementTree, tostring +except ImportError: + from xml.etree.ElementTree import Element, SubElement, ElementTree, \ + tostring + + +class XMLGConfFormat(BaseFormat): + def __init__(self, document_text: str): + processing_methods = OrderedDict({'gconf': self._gconf, + 'entry': self._entry, + 'dir': self._dir, + 'stringvalue': self._stringvalue, + 'default': self._default, + 'local_schema': self._local_schema, + 'li': self._li, + 'longdesc': self._longdesc, + 'unknown': self._unknown}) + super().__init__(processing_methods) + self._format = 'xml_gconf' + + self._initialize_parser() + + self._parse_xml_to_dictionary(document_text) + + def _initialize_parser(self): + action_symbols = (Literal('!') | Literal('-')) + + name = originalTextFor(OneOrMore(Word(printables))) + + self._node_name = Optional(action_symbols)('action') + name('name') + + def _entry(self, xml_element): + try: + element_items = OrderedDict(xml_element.attrib) + + name = element_items.pop('name') + parsing_result = self._node_name.parseString(name) + + if 'value' in element_items: + entry_value = element_items.pop('value') + + elif 'ltype' in element_items: + entry_value = [] + for child in xml_element: + item_to_add = self._processing_methods.get(child.tag, + self._unknown + )(child) + entry_value.append(item_to_add) + else: + entry_value = OrderedDict() + for child in xml_element: + item_to_add = self._processing_methods.get(child.tag, + self._unknown + )(child) + entry_value.update(item_to_add) + element_name = (parsing_result.action, + xml_element.tag, + ('name', parsing_result.name), + *element_items.items()) + return OrderedDict({element_name: entry_value}) + except Exception: + # Какая-то обработка ошибки. + return OrderedDict() + + def _gconf(self, xml_element): + output_dictionary = OrderedDict() + element_name = ('', xml_element.tag) + for child in xml_element: + item_to_add = self._processing_methods.get(child.tag, + self._unknown)(child) + output_dictionary.update(item_to_add) + + return OrderedDict({element_name: output_dictionary}) + + def _dir(self, xml_element): + output_dictionary = OrderedDict() + try: + parsing_result = self._node_name.parseString( + xml_element.attrib['name'] + ) + element_name = (parsing_result.action, + xml_element.tag, + ('name', parsing_result.name)) + except Exception: + # Какая-то обработка ошибки. + return OrderedDict() + + for child in xml_element: + item_to_add = self._processing_methods.get(child.tag, + self._unknown)(child) + output_dictionary.update(item_to_add) + + return OrderedDict({element_name: output_dictionary}) + + def _longdesc(self, xml_element): + element_name = ('', 'longdesc') + description = xml_element.text + + if description is not None: + return OrderedDict({element_name: description}) + else: + # Пустая строка. Возможно ошибка. + # Какая-то обработка ошибки. + return OrderedDict({element_name: ''}) + + def _local_schema(self, xml_element): + output_dictionary = OrderedDict() + try: + element_name = ('', xml_element.tag, + *xml_element.items()) + + except Exception: + # Какая-то обработка ошибки. + return OrderedDict() + + for child in xml_element: + item_to_add = self._processing_methods.get(child.tag, + self._unknown)(child) + output_dictionary.update(item_to_add) + + return OrderedDict({element_name: output_dictionary}) + + def _stringvalue(self, xml_element): + element_name = ('', 'stringvalue') + value = xml_element.text + + if value is not None: + return OrderedDict({element_name: value}) + else: + # Пустая строка. Возможно ошибка. + # Какая-то обработка ошибки. + return OrderedDict({element_name: ''}) + + def _default(self, xml_element): + output_dictionary = OrderedDict() + element_name = ('', xml_element.tag, *xml_element.items()) + + for child in xml_element: + item_to_add = self._processing_methods.get(child.tag, + self._unknown)(child) + output_dictionary.update(item_to_add) + + return OrderedDict({element_name: output_dictionary}) + + def _li(self, xml_element): + child = next(iter(xml_element)) + + list_element = self._processing_methods.get(child.tag, + self._unknown)(child) + # Единственным возможным типом списковых значений пока является string. + string_value = next(iter(list_element.values())) + return string_value + + def _unknown(self, xml_element): + # Действия если элемент неизвестен. + element_name = ('', Element.tag) + return OrderedDict({element_name: 'Unknown element'}) + + def get_document_text(self): + gconf_header = next(iter(self._document_dictionary)) + + root = Element('gconf') + + self._build_section(root, self._document_dictionary[gconf_header]) + + document_tree = ElementTree(root) + xml_document = tostring(document_tree, + encoding="UTF-8", + xml_declaration=True, + pretty_print=True).decode() + return xml_document + + def _build_section(self, current_element, dictionary): + for dict_element in dictionary.keys(): + element_tag = dict_element[1] + element_attributes = OrderedDict({key: value for key, value in + dict_element[2:]}) + + if element_tag == 'dir' or element_tag == 'local_schema'\ + or element_tag == 'default': + include_element = SubElement(current_element, + element_tag, + **element_attributes) + self._build_section(include_element, + dictionary[dict_element]) + + elif element_tag == 'entry': + if isinstance(dictionary[dict_element], OrderedDict): + include_element = SubElement(current_element, + element_tag, + **element_attributes) + + self._build_section(include_element, + dictionary[dict_element]) + elif 'ltype' in element_attributes: + if element_attributes['ltype'] == 'string': + entry_element = SubElement(current_element, + element_tag, + **element_attributes) + for value in dictionary[dict_element]: + list_element = SubElement(entry_element, + 'li', type='string') + include_element = SubElement(list_element, + 'stringvalue') + include_element.text = value + + else: + include_element = SubElement(current_element, + element_tag, + **element_attributes, + value=dictionary[dict_element] + ) + elif element_tag == 'longdesc' or element_tag == 'stringvalue': + include_element = SubElement(current_element, + element_tag) + include_element.text = dictionary[dict_element] diff --git a/calculate/templates/format/xml_xfce_format.py b/calculate/templates/format/xml_xfce_format.py new file mode 100644 index 0000000..a4bb679 --- /dev/null +++ b/calculate/templates/format/xml_xfce_format.py @@ -0,0 +1,164 @@ +# vim: fileencoding=utf-8 +# +from .base_format import BaseFormat +from pyparsing import originalTextFor, Literal, Word, printables, OneOrMore,\ + Optional +from collections import OrderedDict +try: + from lxml.etree import Element, SubElement, ElementTree, tostring +except ImportError: + from xml.etree.ElementTree import Element, SubElement, ElementTree, \ + tostring + + +class XMLXfceFormat(BaseFormat): + def __init__(self, document_text: str, ignore_comments=False): + processing_methods = OrderedDict({'channel': self._channel, + 'property': self._property, + 'value': self._value, + 'unknown': self._unknown}) + super().__init__(processing_methods) + self._format = 'xml_xfce' + + self._initialize_parser() + + if document_text == '': + self._document_dictionary = OrderedDict() + else: + self._parse_xml_to_dictionary(document_text) + + def _initialize_parser(self): + action_symbols = (Literal('!') | Literal('-')) + + name = originalTextFor(OneOrMore(Word(printables))) + + self._node_name = Optional(action_symbols)('action') + name('name') + + # Кортежи с названиями атрибутов различных элементов. + self._ELEMENT_ATTRIBUTES = ('tag', 'name', 'type', 'value') + self._CHANNEL_ATTRIBUTES = ('tag', 'name', 'version') + self._VALUE_ATTRIBUTES = ('tag', 'type', 'value') + + def _property(self, xml_element): + try: + parsing_result = self._node_name.parseString( + xml_element.attrib['name'] + ) + element_name = (parsing_result.action, + xml_element.tag, + parsing_result.name, + xml_element.attrib['type']) + except Exception: + # Какая-то обработка ошибки. + return OrderedDict() + + if xml_element.attrib['type'] == 'empty': + output = OrderedDict() + for child in xml_element: + child_value = self._processing_methods.get(child.tag, + self._unknown + )(child) + output.update(child_value) + + elif xml_element.attrib['type'] == 'array': + output = [] + for child in xml_element: + child_value = self._processing_methods.get(child.tag, + self._unknown + )(child) + output.append(child_value) + else: + try: + output = xml_element.attrib['value'] + except KeyError: + # Какая-то обработка ошибки. + return OrderedDict() + + return OrderedDict({element_name: output}) + + def _value(self, xml_element): + try: + value = (xml_element.tag, + xml_element.attrib['type'], + xml_element.attrib['value']) + except KeyError: + # Какая-то обработка ошибки. + return ('',) + return value + + def _channel(self, xml_element): + output_dictionary = OrderedDict() + try: + parsing_result = self._node_name.parseString( + xml_element.attrib['name'] + ) + element_name = (parsing_result.action, + xml_element.tag, + parsing_result.name, + xml_element.attrib['version']) + except Exception: + # Какая-то обработка ошибки. + return OrderedDict() + + for child in xml_element: + item_to_add = self._processing_methods.get(child.tag, + self._unknown)(child) + output_dictionary.update(item_to_add) + + return OrderedDict({element_name: output_dictionary}) + + def _unknown(self, xml_element): + # Действия если элемент неизвестен. + element_name = ('', xml_element.tag) + return OrderedDict({element_name: 'Unknown element'}) + + def get_document_text(self): + channel = next(iter(self._document_dictionary.keys())) + channel_head = OrderedDict( + {key: value for key, value in + zip(self._CHANNEL_ATTRIBUTES, channel[1:])} + ) + + root = Element(channel_head.pop('tag'), **channel_head) + + self._build_section(root, self._document_dictionary[channel]) + document_tree = ElementTree(root) + xml_document = tostring(document_tree, + encoding="UTF-8", + xml_declaration=True, + pretty_print=True).decode() + + return xml_document + + def _build_section(self, current_element, dictionary): + for dict_element in dictionary.keys(): + element_head = OrderedDict({key: value for key, value in + zip(self._ELEMENT_ATTRIBUTES, + dict_element[1:])}) + + if element_head['type'] == 'empty': + include_element = SubElement(current_element, + element_head.pop('tag'), + **element_head) + self._build_section(include_element, + dictionary[dict_element]) + + elif element_head['type'] == 'array': + include_element = SubElement(current_element, + element_head.pop('tag'), + **element_head) + + for list_element in dictionary[dict_element]: + list_element_head = OrderedDict( + {key: value for key, value in + zip(self._VALUE_ATTRIBUTES, + list_element)} + ) + SubElement(include_element, + list_element_head.pop('tag'), + **list_element_head) + else: + SubElement(current_element, + element_head.pop('tag'), + **element_head, + value=dictionary[dict_element]) diff --git a/calculate/utils/__init__.py b/calculate/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calculate/utils/files.py b/calculate/utils/files.py new file mode 100644 index 0000000..7cefa06 --- /dev/null +++ b/calculate/utils/files.py @@ -0,0 +1,261 @@ +# vim: fileencoding=utf-8 +# +from subprocess import Popen, PIPE +from io import TextIOWrapper +from os import path +import os + + +class FilesError(Exception): + pass + + +class PipeProcess(): + def _get_stdout(self): + return PIPE + + def close(self): + pass + + @property + def shell_command(self): + return '' + + +class KeyboardInputProcess(): + def _get_stdout(self): + return None + + def close(self): + pass + + @property + def shell_command(self): + return '' + + +class Process(): + def __init__(self, command, *parameters, **kwargs): + if 'stdin' not in kwargs: + self._stdin = PipeProcess() + elif kwargs['stdin'] == PIPE: + self._stdin = PipeProcess() + elif kwargs['stdin'] is None: + self._stdin = KeyboardInputProcess() + else: + self._stdin = kwargs['stdin'] + + self._stdout = kwargs.get('stdout', PIPE) + self._stderr = kwargs.get('stderr', PIPE) + + self._envdict = kwargs.get('envdict', os.environ.copy()) + self._envdict['LANG'] = kwargs.get('lang', 'C') + + self._timeout = kwargs.get('timeout', None) + self._cwd = kwargs.get('cwd', None) + + self._command = _get_program_path(command) + if not self._command: + raise FilesError("command not found '{}'".format(command)) + self._command = [self._command, *parameters] + + self._process = None + self._iterator = iter([]) + + # Flags. + self._opened = False + self._is_read = False + + self._readable = False + self._writable = False + self._readable_errors = False + + # I/O handlers. + self.stdin_handler = None + self.stdout_handler = None + self.stderr_handler = None + + # Caches. + self._output_cache = '' + self._error_cache = '' + + def _get_stdout(self): + self._open_process() + return self.stdout_handler + + def _get_stdin(self): + return self.stdin_handler + + def _open_process(self): + try: + piped_stdin = self._stdin._get_stdout() + self._process = Popen(self._command, + stdout=self._stdout, + stdin=piped_stdin, + stderr=self._stderr, + cwd=self._cwd, + close_fds=True, + env=self._envdict) + + if piped_stdin == PIPE: + self.stdin_handler = TextIOWrapper(self._process.stdin, + encoding='utf8') + self._writable = True + elif piped_stdin is not None: + self.stdin_handler = self._stdin._get_stdin() + self._writable = True + + if self._stdout == PIPE: + self.stdout_handler = TextIOWrapper(self._process.stdout, + encoding='utf8') + self._readable = True + + if self._stderr == PIPE: + self.stderr_handler = TextIOWrapper(self._process.stderr, + encoding='utf8') + self._readable_errors = True + + self._opened = True + except Exception as error: + print('error:', error) + raise FilesError('Can not open process.') + + def close(self): + if self._opened: + if self._process.stdin: + self.stdin_handler.close() + self._stdin.close() + self._opened = False + + def write(self, data): + if not self._opened: + self._open_process() + self._is_read = False + self._output_cache = '' + try: + if self._writable: + self.stdin_handler.write(data) + self.stdin_handler.flush() + else: + raise FilesError('Process stdin is not writable.') + except IOError as error: + raise FilesError(str(error)) + + def read(self): + if not self._opened and not self._writable: + self._open_process() + + if not self._readable: + raise FilesError('Process is not readable.') + try: + if not self._is_read: + if self._writable: + self.close() + + if self._readable: + self._output_cache = self.stdout_handler.read() + + if self._readable_errors: + self._error_cache = self.stderr_handler.read() + + self._process.poll() + self._is_read = True + return self._output_cache + except KeyboardInterrupt: + self.kill() + raise + + def read_error(self): + self.read() + if not self._error_cache: + try: + self._error_cache = self.stderr_handler.read() + except IOError: + self._error_cache = '' + return self._error_cache + + def kill(self): + if self._opened: + self._process.kill() + + def read_lines(self): + return self.read().split('\n') + + def __iter__(self): + if not self._iterator: + self._iterator = iter(self.read_lines()) + return self._iterator + + def next(self): + return next(self.__iter__(), None) + + @property + def writable(self): + return self._writable + + @property + def readable(self): + return self._readable + + @property + def readable_errors(self): + return self._readable_errors + + def return_code(self): + self.read() + return self._process.returncode + + @property + def shell_command(self): + command = ' '.join(self._command) + previous_commands = self._stdin.shell_command + if previous_commands == '': + return command + else: + return ' | '.join([previous_commands, command]) + + def success(self): + return self.return_code() == 0 + + def failed(self): + return self.return_code() != 0 + + +class ProgramPathCache(): + def __init__(self): + self._cache = {} + + def __call__(self, program_name, prefix='/'): + program_base_name = path.basename(program_name) + PATH = os.environ['PATH'] + PATH = PATH.split(':') + cache_key = (program_base_name, prefix) + + if cache_key in self._cache: + self._cache[cache_key] + + if program_name.startswith('/'): + if path.exists(join_paths(prefix, program_name)): + self._cache[cache_key] = program_name + return program_name + for program_name in (join_paths(bin_path, program_base_name) + for bin_path in PATH): + if path.exists(join_paths(prefix, program_name)): + self._cache[cache_key] = program_name + return program_name + return False + + +_get_program_path = ProgramPathCache() + + +def join_paths(*paths): + if len(paths) == 1: + return next(iter(paths)) + + paths_to_join = filter(lambda path: path.strip() and path != "/", + map(lambda path: + path[1:] if path.startswith('/') else path, + paths[1:])) + output_path = path.join(paths[0], *paths_to_join) + return output_path diff --git a/calculate/vars/.datavars.py.swp b/calculate/vars/.datavars.py.swp new file mode 100644 index 0000000..c03b12f Binary files /dev/null and b/calculate/vars/.datavars.py.swp differ diff --git a/calculate/vars/__init__.py b/calculate/vars/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calculate/vars/datavars.py b/calculate/vars/datavars.py new file mode 100644 index 0000000..972a523 --- /dev/null +++ b/calculate/vars/datavars.py @@ -0,0 +1,51 @@ +import site +import os + +class Variable: + def __init__(self): + pass + + def get(self): + """ + Функция заполнения переменной + """ + pass + + def set(self, value): + """ + Функция модификации переменной + """ + pass + + def choice(self): + """ + Функция возвращет список доступных значений для переменной + """ + pass + + def check(self, value): + """ + Функция проверки значения устанавливаемого значения + """ + +class NamespaceError(Exception): + pass + +class Namespace: + DefaultPath = "[default_path]" + def __init__(self, varPath=DefaultPath): + if varPath is Namespace.DefaultPath: + self.varPath = self._getMainPackage() + else: + self.varPath = varPath + self.childs = [] + + def _getMainPackage(self): + site_dirs = [os.path.normpath(x) for x in site.getsitepackages()] + for site_dir in site_dirs: + calculate_dir = os.path.join(site_dir, "calculate/vars") + if os.path.exists(calculate_dir): + return calculate_dir + + def findVariablePackages(self, dirPath): + pass diff --git a/calculate/vars/os/__init__.py b/calculate/vars/os/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/calculate/vars/os/gentoo/__init__.py b/calculate/vars/os/gentoo/__init__.py new file mode 100644 index 0000000..d3c252f --- /dev/null +++ b/calculate/vars/os/gentoo/__init__.py @@ -0,0 +1,4 @@ +from calculate.vars.datavars import Variable + +class Profile(Variable): + pass diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..bf258f3 --- /dev/null +++ b/conftest.py @@ -0,0 +1,164 @@ +import pytest +from collections import OrderedDict + + +@pytest.fixture(scope='function') +def StringDictionaries(): + ParamLine1 = OrderedDict({'parameter name1': 'value1'}) + ParamLine2 = OrderedDict({'parameter name2': 'value2'}) + ParamLine3 = OrderedDict({'parameter name3': 'value3'}) + + Section1 = OrderedDict({'section name1': OrderedDict(**ParamLine1, + **ParamLine2)}) + Section2 = OrderedDict({'section name2': OrderedDict(**ParamLine1, + **ParamLine2)}) + Section3 = OrderedDict({'section name3': OrderedDict(**ParamLine3)}) + + OriginalDictionary = OrderedDict(**Section1, **Section2) + TemplateDictionary = OrderedDict(**Section3) + ResultDictionary = OrderedDict(**Section1, **Section2, **Section3) + + return (OriginalDictionary, TemplateDictionary, ResultDictionary) + + +@pytest.fixture(scope='function') +def TupleDictionaries(): + ParamLine1 = OrderedDict({('', 'parameter name1'): 'value1'}) + ParamLine2 = OrderedDict({('', 'parameter name2'): 'value2'}) + ParamLine3 = OrderedDict({('', 'parameter name3'): 'value3'}) + + Section1 = OrderedDict({('', 'section name1'): OrderedDict(**ParamLine1, + **ParamLine2)}) + Section2 = OrderedDict({('', 'section name2'): OrderedDict(**ParamLine1, + **ParamLine2)}) + Section3 = OrderedDict({('', 'section name3'): OrderedDict(**ParamLine3)}) + + OriginalDictionary = OrderedDict(**Section1, **Section2) + TemplateDictionary = OrderedDict(**Section3) + ResultDictionary = OrderedDict(**Section1, **Section2, **Section3) + + return (OriginalDictionary, TemplateDictionary, ResultDictionary) + + +@pytest.fixture(scope='function') +def MergeSectionDictionaries(): + ParamLine1 = OrderedDict({('', 'parameter name1'): 'value1'}) + ParamLine2 = OrderedDict({('', 'parameter name2'): 'value2'}) + ParamLine3 = OrderedDict({('', 'parameter name3'): 'value3'}) + + Section1 = OrderedDict({('', 'section name1'): OrderedDict(**ParamLine1, + **ParamLine2)}) + Section2 = OrderedDict({('', 'section name2'): OrderedDict(**ParamLine1, + **ParamLine2)}) + Section2Add = OrderedDict({('', 'section name2'): + OrderedDict(**ParamLine3)}) + Section2Added = OrderedDict({('', 'section name2'): + OrderedDict(**ParamLine1, + **ParamLine2, + **ParamLine3)}) + + OriginalDictionary = OrderedDict(**Section1, **Section2) + TemplateDictionary = OrderedDict(**Section2Add) + ResultDictionary = OrderedDict(**Section1, **Section2Added) + + return (OriginalDictionary, TemplateDictionary, ResultDictionary) + + +@pytest.fixture(scope='function') +def ChangeParameterDictionaries(): + ParamLine1 = OrderedDict({('', 'parameter name1'): 'value1'}) + ParamLine2 = OrderedDict({('', 'parameter name2'): 'value2'}) + ParamLine3 = OrderedDict({('', 'parameter name3'): 'value3'}) + ParamLine2New = OrderedDict({('', 'parameter name2'): 'NewValue'}) + ParamLine3New = OrderedDict({('', 'parameter name3'): 'OtherValue'}) + + Section = OrderedDict({('', 'section name1'): OrderedDict(**ParamLine1, + **ParamLine2, + **ParamLine3)}) + SectionNew = OrderedDict({('', 'section name1'): + OrderedDict(**ParamLine1, + **ParamLine2New, + **ParamLine3New)}) + + OriginalDictionary = OrderedDict(**Section) + TemplateDictionary = OrderedDict(**SectionNew) + ResultDictionary = TemplateDictionary + + return (OriginalDictionary, TemplateDictionary, ResultDictionary) + + +@pytest.fixture(scope='function') +def DeleteSectionDictionaries(): + ParamLine1 = OrderedDict({('', 'parameter name1'): 'value1'}) + ParamLine2 = OrderedDict({('', 'parameter name2'): 'value2'}) + + Section1 = OrderedDict({('', 'section name1'): OrderedDict(**ParamLine1, + **ParamLine2)}) + Section2 = OrderedDict({('', 'section name2'): OrderedDict(**ParamLine1, + **ParamLine2)}) + SectionDel = OrderedDict({('!', 'section name2'): OrderedDict()}) + + OriginalDictionary = OrderedDict(**Section1, **Section2) + TemplateDictionary = OrderedDict(**SectionDel) + ResultDictionary = OrderedDict(**Section1) + + return (OriginalDictionary, TemplateDictionary, ResultDictionary) + + +@pytest.fixture(scope='function') +def ReplaceSectionDictionaries(): + ParamLine1 = OrderedDict({('', 'parameter name1'): 'value1'}) + ParamLine2 = OrderedDict({('', 'parameter name2'): 'value2'}) + ParamLine3 = OrderedDict({('', 'parameter name3'): 'value3'}) + ParamLine4 = OrderedDict({('', 'parameter name4'): 'value4'}) + + Section1 = OrderedDict({('', 'section name1'): OrderedDict(**ParamLine1, + **ParamLine2)}) + Section2 = OrderedDict({('', 'section name2'): OrderedDict(**ParamLine1, + **ParamLine2)}) + Section2Replace = OrderedDict({('-', 'section name2'): + OrderedDict(**ParamLine3, **ParamLine4)}) + Section2Replaced = OrderedDict({('', 'section name2'): + OrderedDict(**ParamLine3, **ParamLine4)}) + + OriginalDictionary = OrderedDict(**Section1, **Section2) + TemplateDictionary = OrderedDict(**Section2Replace) + ResultDictionary = OrderedDict(**Section1, **Section2Replaced) + + return (OriginalDictionary, TemplateDictionary, ResultDictionary) + + +@pytest.fixture(scope='function') +def DeleteParameterDictionaries(): + ParamLine1 = OrderedDict({('', 'parameter name1'): 'value1'}) + ParamLine2 = OrderedDict({('', 'parameter name2'): 'value2'}) + ParamLineDel = OrderedDict({('!', 'parameter name2'): 'value2'}) + + Section1 = OrderedDict({('', 'section name1'): OrderedDict(**ParamLine1, + **ParamLine2)}) + Section2 = OrderedDict({('', 'section name2'): OrderedDict(**ParamLine1, + **ParamLine2)}) + SectionDel = OrderedDict({('', 'section name2'): + OrderedDict(**ParamLineDel)}) + SectionAfterDel = OrderedDict({('', 'section name2'): + OrderedDict(**ParamLine1)}) + + OriginalDictionary = OrderedDict(**Section1, **Section2) + TemplateDictionary = OrderedDict(**SectionDel) + ResultDictionary = OrderedDict(**Section1, **SectionAfterDel) + + return (OriginalDictionary, TemplateDictionary, ResultDictionary) + + +@pytest.fixture(scope='function') +def DictionariesWithoutSections(): + ParamLine1 = OrderedDict({('', 'parameter name1'): 'value1'}) + ParamLine2 = OrderedDict({('', 'parameter name2'): 'value2'}) + ParamLine3 = OrderedDict({('', 'parameter name3'): 'value3'}) + ParamLineDel = OrderedDict({('!', 'parameter name2'): ''}) + + OriginalDictionary = OrderedDict(**ParamLine1, **ParamLine2) + TemplateDictionary = OrderedDict(**ParamLine3, **ParamLineDel) + ResultDictionary = OrderedDict(**ParamLine1, **ParamLine3) + + return (OriginalDictionary, TemplateDictionary, ResultDictionary) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..7744e26 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,23 @@ +# vim: fileencoding=utf-8 +# pytest.ini +[pytest] +markers = + vars: marker for running tests for datavars + base: marker for running tests for base format class. + bind: marker for running tests for bind format. + compiz: marker for running tests for compiz format. + diff: marker for running test for diff format. + dovecot: marker for running tests for devecot format. + json: marker for running tests for json format. + kde: marker for running test for kde format. + kernel: marker for running test for kernel format. + ldap: marker for running test for ldap format. + openrc: marker for running test for openrc format. + patch: marker for running test fot patch format. + postfix: marker for running test for postfix format. + procmail: marker for running test for procmail format. + proftpd: marker for running tests for proftpd format. + samba: marker for running tests for samba format. + xml_xfce: marker for running tests for xml xfce format. + xml_gconf: marker for running tests for xml gconf format. + files: marker for running tests for calculate.utils.files module. diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..7d4986d --- /dev/null +++ b/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup, find_packages +from os.path import join, dirname + +setup( + name='calculate-lib', + version='1.0', + packages=find_packages(), +) diff --git a/tests/format/__init__.py b/tests/format/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/format/test_base.py b/tests/format/test_base.py new file mode 100644 index 0000000..da1e6db --- /dev/null +++ b/tests/format/test_base.py @@ -0,0 +1,83 @@ +import pytest +from calculate.templates.format.base_format import BaseFormat + + +@pytest.mark.base +class TestJoinMethod: + def test_if_inputs_are_dictionaries_with_string_keys_without_any_action_marks__the_dictionaties_just_merged(self, StringDictionaries): + BaseObject = BaseFormat([]) + Original, Template, Result = StringDictionaries + BaseObject._join(Original, Template, join_before=False) + assert Original == Result + + def test_if_inputs_are_dictionaries_with_tuple_keys_without_any_action_marks_as_their_keys__the_dictionaries_just_merged(self, TupleDictionaries): + BaseObject = BaseFormat([]) + Original, Template, Result = TupleDictionaries + BaseObject._join(Original, Template, join_before=False) + assert Original == Result + + def test_if_inputs_are_dictionaries_with_same_sections_which_contain_different_parameters__a_section_from_the_template_added_to_the_same_section_of_original_dictionary(self, MergeSectionDictionaries): + BaseObject = BaseFormat([]) + Original, Template, Result = MergeSectionDictionaries + BaseObject._join(Original, Template, join_before=False) + assert Original == Result + + def test_if_inputs_are_dictionaries_with_parameters_with_same_name_in_same_section__parameters_values_in_original_dictionary_changed_to_values_from_template(self, ChangeParameterDictionaries): + BaseObject = BaseFormat([]) + Original, Template, Result = ChangeParameterDictionaries + BaseObject._join(Original, Template, join_before=False) + assert Original == Result + + def test_if_input_template_dictionary_has_delete_mark_for_section__section_will_be_deleted(self, DeleteSectionDictionaries): + BaseObject = BaseFormat([]) + Original, Template, Result = DeleteSectionDictionaries + BaseObject._join(Original, Template, join_before=False) + assert Original == Result + + def test_if_input_template_dictionary_has_replace_mark_for_section__section_will_be_deleted(self, ReplaceSectionDictionaries): + BaseObject = BaseFormat([]) + Original, Template, Result = ReplaceSectionDictionaries + BaseObject._join(Original, Template, join_before=False) + assert Original == Result + + def test_if_input_template_dictionary_has_delete_mark_for_parameter__parameter_will_be_deleted(self, DeleteParameterDictionaries): + BaseObject = BaseFormat([]) + Original, Template, Result = DeleteParameterDictionaries + BaseObject._join(Original, Template, join_before=False) + assert Original == Result + + def test_if_input_dictionaries_have_no_sections_and_have_only_parameter_lines__it_will_be_processed_correctly(self, DictionariesWithoutSections): + BaseObject = BaseFormat([]) + Original, Template, Result = DictionariesWithoutSections + BaseObject._join(Original, Template, join_before=False) + assert Original == Result + + +@pytest.mark.base +class TestLogicLinesMethod: + def test_if_input_is_text_document_the_method_returns_list_of_its_lines(self): + with open('./tests/format/testfiles/logic_lines_test.txt', 'r') as InputFile: + InputText = InputFile.read() + + processingMethods = [] + OutputLines = ['First string of test file.', + 'Second string of test file.', + 'Third string of test file.', + 'Fourth string of test file.'] + + BaseObject = BaseFormat(processingMethods) + InputLines = BaseObject._get_list_of_logic_lines(InputText) + assert InputLines == OutputLines + + + def test_if_lines_in_document_divided_using_backslash_as_continuation_symbol__method_returns_list_of_full_lines(self): + with open('./tests/format/testfiles/logic_lines_test_input.txt', 'r') as InputFile: + InputText = InputFile.read() + + with open('./tests/format/testfiles/logic_lines_test_output.txt', 'r') as OutputFile: + OutputText = OutputFile.read() + + BaseObject = BaseFormat([]) + InputLines = BaseObject._get_list_of_logic_lines(InputText) + OutputLines = BaseObject._get_list_of_logic_lines(OutputText) + assert InputLines == OutputLines diff --git a/tests/format/test_bind.py b/tests/format/test_bind.py new file mode 100644 index 0000000..6779521 --- /dev/null +++ b/tests/format/test_bind.py @@ -0,0 +1,298 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.bind_format import BINDFormat + + +@pytest.mark.bind +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = '''directory "/var/bind"; +pid-file "/run/named/named.pid"; +disable-empty-zone "10.in-addr.arpa"; + ''' + + result = OrderedDict({ + ('', 'directory'): + ['"/var/bind"'], + ('', 'pid-file'): + ['"/run/named/named.pid"'], + ('', 'disable-empty-zone'): + ['"10.in-addr.arpa"'] + }) + + bind_object = BINDFormat(document_text) + assert bind_object._document_dictionary == result + + def test_if_input_document_contains_some_block_of_parameters__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' +acl "dns_servers" { + 127.0.0.1; + 10.0.1.3; + 10.1.0.3; +}; + +options { + response-policy { + zone "rpz.zone"; + }; + recursion yes; +}; + +zone "localhost" IN { + type master; + file "pri/localhost.zone"; + notify no; +} + ''' + + acl_section = OrderedDict({('', '127.0.0.1'): [''], + ('', '10.0.1.3'): [''], + ('', '10.1.0.3'): ['']}) + response_section = OrderedDict({('', 'zone'): ['"rpz.zone"']}) + options_section = OrderedDict({('', 'response-policy'): response_section, + ('', 'recursion'): ['yes']}) + zone_section = OrderedDict({('', 'type'): ['master'], + ('', 'file'): ['"pri/localhost.zone"'], + ('', 'notify'): ['no']}) + result = OrderedDict({('', 'acl', '"dns_servers"'): acl_section, + ('', 'options'): options_section, + ('', 'zone', '"localhost"', 'IN'): zone_section}) + + bind_object = BINDFormat(document_text) + assert bind_object._document_dictionary == result + + def test_if_input_document_contains_some_blocks_with_similar_names__the_blocks_join_recursively(self): + document_text = ''' +acl "dns_servers" { + 127.0.0.1; + 10.0.1.3; + 10.1.0.3; +}; + +options { + response-policy { + mood "almost.blue"; + }; + todo "drink.beer" +}; + +acl "dns_servers" { + 10.3.0.3; + 10.4.0.3; +}; + +options { + response-policy { + zone "rpz.zone"; + }; +} +''' + acl_section = OrderedDict({('', '127.0.0.1'): [''], + ('', '10.0.1.3'): [''], + ('', '10.1.0.3'): [''], + ('', '10.3.0.3'): [''], + ('', '10.4.0.3'): ['']}) + response_section = OrderedDict({('', 'mood'): ['"almost.blue"'], + ('', 'zone'): ['"rpz.zone"']}) + options_section = OrderedDict({('', 'response-policy'): + response_section, + ('', 'todo'): ['"drink.beer"']}) + + result = OrderedDict({('', 'acl', '"dns_servers"'): acl_section, + ('', 'options'): options_section}) + + bind_object = BINDFormat(document_text) + assert bind_object._document_dictionary == result + + def test_if_input_document_contains_blocks_and_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = ''' +!pid-file "/run/named/named.pid"; +-disable-empty-zone "10.in-addr.arpa"; + +acl "dns_servers" { + !127.0.0.1; + 10.0.1.3; + 10.1.0.3; +}; + +-options { + !response-policy { + zone "rpz.zone"; + }; + !recursion yes; +} +''' + acl_section = OrderedDict({('!', '127.0.0.1'): [''], + ('', '10.0.1.3'): [''], + ('', '10.1.0.3'): ['']}) + response_section = OrderedDict({('', 'zone'): ['"rpz.zone"']}) + options_section = OrderedDict({('!', 'response-policy'): + response_section, + ('!', 'recursion'): ['yes']}) + + result = OrderedDict({('!', 'pid-file'): + ['"/run/named/named.pid"'], + ('-', 'disable-empty-zone'): + ['"10.in-addr.arpa"'], + ('', 'acl', '"dns_servers"'): acl_section, + ('-', 'options'): options_section}) + + bind_object = BINDFormat(document_text) + assert bind_object._document_dictionary == result + + def test_if_parameters_and_blocks_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value_or_with_special_key_in_block_dictionary(self): + document_text = ''' +// Comment 1 +!pid-file "/run/named/named.pid"; +/* +* A very big comment. +* Still here... +* The pure giant of the comment kind. +*/ +-disable-empty-zone "10.in-addr.arpa"; + +# Comment 2 +// Comment 3 +acl "dns_servers" { + !127.0.0.1; + // Comment 4 + 10.0.1.3; + 10.1.0.3; +}; + +-options { + !response-policy { + /* + * This comment is very important. + * And I have no idea, why this + * comment is so important. + */ + zone "rpz.zone"; + }; + !recursion yes; +} + ''' + + acl_section = OrderedDict({'#': ['# Comment 2', '// Comment 3'], + ('!', '127.0.0.1'): [''], + ('', '10.0.1.3'): ['// Comment 4', ''], + ('', '10.1.0.3'): ['']}) + response_section = OrderedDict({('', 'zone'): + ['/*', + '* This comment is very important.', + '* And I have no idea, why this', + '* comment is so important.', + '*/', + '"rpz.zone"']}) + options_section = OrderedDict({('!', 'response-policy'): + response_section, + ('!', 'recursion'): ['yes']}) + + result = OrderedDict({('!', 'pid-file'): + ['// Comment 1', + '"/run/named/named.pid"'], + ('-', 'disable-empty-zone'): + ['/*', + '* A very big comment.', + '* Still here...', + '* The pure giant of the comment kind.', + '*/', + '"10.in-addr.arpa"'], + ('', 'acl', '"dns_servers"'): acl_section, + ('-', 'options'): options_section}) + + bind_object = BINDFormat(document_text) + assert bind_object._document_dictionary == result + + def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = ''' +// Comment 1 +!pid-file "/run/named/named.pid"; +/* +* A very big comment. +* Still here... +* The pure giant of comment kind. +*/ +-disable-empty-zone "10.in-addr.arpa"; + +# Comment 2 +// Comment 3 +acl "dns_servers" { + !127.0.0.1; + // Comment 4 + 10.0.1.3; + 10.1.0.3; +}; + +-options { + !response-policy { + /* + * This comment is very important. + * And I have no idea, why this + * comment is so important. + */ + zone "rpz.zone"; + }; + !recursion yes; +} + ''' + + acl_section = OrderedDict({('!', '127.0.0.1'): [''], + ('', '10.0.1.3'): [''], + ('', '10.1.0.3'): ['']}) + response_section = OrderedDict({('', 'zone'): ['"rpz.zone"']}) + options_section = OrderedDict({('!', 'response-policy'): + response_section, + ('!', 'recursion'): ['yes']}) + + result = OrderedDict({('!', 'pid-file'): + ['"/run/named/named.pid"'], + ('-', 'disable-empty-zone'): + ['"10.in-addr.arpa"'], + ('', 'acl', '"dns_servers"'): acl_section, + ('-', 'options'): options_section}) + + bind_object = BINDFormat(document_text, ignore_comments=True) + assert bind_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_values_or_with_empty_block__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = ''' +!pid-file; + +!acl "dns_servers" {}; + +options { + !response-policy {}; + !recursion; +} + ''' + + options_section = OrderedDict({('!', 'response-policy'): + OrderedDict(), + ('!', 'recursion'): ['']}) + + result = OrderedDict({('!', 'pid-file'): + [''], + ('!', 'acl', '"dns_servers"'): OrderedDict(), + ('', 'options'): options_section}) + + bind_object = BINDFormat(document_text) + assert bind_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/bind_original.conf', 'r') as original_file: + original_text = original_file.read() + print(original_text) + bind_original_object = BINDFormat(original_text) + + with open('./tests/format/testfiles/bind_template.conf', 'r') as template_file: + template_text = template_file.read() + bind_template_object = BINDFormat(template_text, + ignore_comments=True) + + bind_original_object.join_template(bind_template_object) + + with open('./tests/format/testfiles/bind_result.conf', 'r') as result_file: + result_text = result_file.read() + + assert bind_original_object.get_document_text() == result_text diff --git a/tests/format/test_compiz.py b/tests/format/test_compiz.py new file mode 100644 index 0000000..7894d30 --- /dev/null +++ b/tests/format/test_compiz.py @@ -0,0 +1,218 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.compiz_format import CompizFormat +from pprint import pprint + + +@pytest.mark.compiz +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' + [Added Associations] + application/illustrator=zzz-gimp.desktop + application/pdf=evince.desktop; + application/rtf=libreoffice-writer.desktop; + application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; + ''' + + section = OrderedDict({('', 'application/illustrator'): + ['zzz-gimp.desktop'], + ('', 'application/pdf'): + ['evince.desktop;'], + ('', 'application/rtf'): + ['libreoffice-writer.desktop;'], + ('', 'application/vnd.oasis.opendocument.spreadsheet'): + ['calculate-calc.desktop;']}) + + result = OrderedDict({('', 'Added Associations'): section}) + + compiz_object = CompizFormat(document_text) + assert compiz_object._document_dictionary == result + + def test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary(self): + document_text = ''' + [Added Associations] + + application/illustrator=zzz-gimp.desktop + application/pdf=evince.desktop; + + + application/rtf=libreoffice-writer.desktop; + + application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; + ''' + + section = OrderedDict({('', 'application/illustrator'): + ['zzz-gimp.desktop'], + ('', 'application/pdf'): + ['evince.desktop;'], + ('', 'application/rtf'): + ['libreoffice-writer.desktop;'], + ('', 'application/vnd.oasis.opendocument.spreadsheet'): + ["calculate-calc.desktop;"]}) + + result = OrderedDict({('', 'Added Associations'): section}) + + compiz_object = CompizFormat(document_text) + assert compiz_object._document_dictionary == result + + def test_if_input_document_contains_sections_with_different_names_but_different_parameters__the_parameters_merged_in_one_section(self): + document_text = ''' + [Added Associations] + application/illustrator=zzz-gimp.desktop + application/pdf=evince.desktop; + [Added Associations] + application/rtf=libreoffice-writer.desktop; + application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; + ''' + + section = OrderedDict({('', 'application/illustrator'): + ['zzz-gimp.desktop'], + ('', 'application/pdf'): + ['evince.desktop;'], + ('', 'application/rtf'): + ['libreoffice-writer.desktop;'], + ('', 'application/vnd.oasis.opendocument.spreadsheet'): + ['calculate-calc.desktop;']}) + + result = OrderedDict({('', 'Added Associations'): section}) + + compiz_object = CompizFormat(document_text) + assert compiz_object._document_dictionary == result + + def test_if_input_document_contains_sections_with_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = ''' + [Added Associations] + !application/illustrator=zzz-gimp.desktop + -application/pdf=evince.desktop; + !application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; + ''' + + section = OrderedDict({('!', 'application/illustrator'): + ['zzz-gimp.desktop'], + ('-', 'application/pdf'): + ['evince.desktop;'], + ('!', 'application/vnd.oasis.opendocument.spreadsheet'): + ["calculate-calc.desktop;"]}) + + result = OrderedDict({('', 'Added Associations'): section}) + + compiz_object = CompizFormat(document_text) + assert compiz_object._document_dictionary == result + + def test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value(self): + document_text = ''' + # Comment + [Added Associations] + # Comment1 + application/illustrator=zzz-gimp.desktop + application/pdf=evince.desktop; + + # Comment2 + # Comment3 + + application/rtf=libreoffice-writer.desktop; + + [Other Section] + #Comment + !application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; + ''' + + section_1 = OrderedDict({'#': ['# Comment'], + ('', 'application/illustrator'): + ['# Comment1', + 'zzz-gimp.desktop'], + ('', 'application/pdf'): + ['evince.desktop;'], + ('', 'application/rtf'): + ['# Comment2', + '# Comment3', + 'libreoffice-writer.desktop;']}) + + section_2 = OrderedDict({('!', 'application/vnd.oasis.opendocument.spreadsheet'): + ['#Comment', + "calculate-calc.desktop;"]}) + + result = OrderedDict({('', 'Added Associations'): section_1, + ('', 'Other Section'): section_2}) + + compiz_object = CompizFormat(document_text) + assert compiz_object._document_dictionary == result + + def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = ''' + # Comment + [Added Associations] + # Comment1 + application/illustrator=zzz-gimp.desktop + application/pdf=evince.desktop; + + # Comment2 + # Comment3 + + application/rtf=libreoffice-writer.desktop; + + [Other Section] + #Comment + !application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; + ''' + + section_1 = OrderedDict({('', 'application/illustrator'): + ['zzz-gimp.desktop'], + ('', 'application/pdf'): + ['evince.desktop;'], + ('', 'application/rtf'): + ['libreoffice-writer.desktop;']}) + + section_2 = OrderedDict({('!', 'application/vnd.oasis.opendocument.spreadsheet'): + ["calculate-calc.desktop;"]}) + + result = OrderedDict({('', 'Added Associations'): section_1, + ('', 'Other Section'): section_2}) + + compiz_object = CompizFormat(document_text, ignore_comments=True) + assert compiz_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values_and_sections_to_delete__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = ''' + [-Added Associations] + # Comment + !application/illustrator=zzz-gimp.desktop + !application/pdf= + !application/rtf + + [!Other Section] + application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; + ''' + + section_1 = OrderedDict({('!', 'application/illustrator'): + ['# Comment', + 'zzz-gimp.desktop'], + ('!', 'application/pdf'): [], + ('!', 'application/rtf'): []}) + + section_2 = OrderedDict({('', 'application/vnd.oasis.opendocument.spreadsheet'): + ["calculate-calc.desktop;"]}) + + result = OrderedDict({('-', 'Added Associations'): section_1, + ('!', 'Other Section'): section_2}) + + compiz_object = CompizFormat(document_text) + assert compiz_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/compiz_original', 'r') as original_file: + original_text = original_file.read() + compiz_original_object = CompizFormat(original_text) + + with open('./tests/format/testfiles/compiz_template', 'r') as template_file: + template_text = template_file.read() + compiz_template_object = CompizFormat(template_text, + ignore_comments=True) + + compiz_original_object.join_template(compiz_template_object) + + with open('./tests/format/testfiles/compiz_result', 'r') as result_file: + result_text = result_file.read() + + assert compiz_original_object.get_document_text() == result_text diff --git a/tests/format/test_diff.py b/tests/format/test_diff.py new file mode 100644 index 0000000..cc3575b --- /dev/null +++ b/tests/format/test_diff.py @@ -0,0 +1,97 @@ +import pytest +from calculate.templates.format.diff_format import DiffFormat +from calculate.utils.files import Process +from os import path +import os + + +@pytest.mark.diff +class TestExecuteMethods: + def test_if_diff_patch_used_for_patching_of_several_files__it_changes_patched_file_correctly(self): + test_result = True + root_path = path.join(os.getcwd(), 'tests/format/testfiles/') + with open(path.join(root_path, 'diff_1.patch')) as patch_file: + patch_text = patch_file.read() + + diff_patch = DiffFormat(patch_text) + print('Path:', root_path) + output = diff_patch.execute_format(root_path=root_path) + if output: + print('Changed files:') + for changed_file in diff_patch._changed_files_list: + print(changed_file, ':', sep='') + with open(path.join(diff_patch._root_path, changed_file)) as patched_file: + patched_file_text = patched_file.read() + print(patched_file_text) + other_file_name = 'b' + changed_file[1:] + with open(path.join(diff_patch._root_path, + other_file_name)) as other_file: + other_file_text = other_file.read() + test_result = test_result and (other_file_text == patched_file_text) + + return_patch_run = Process('patch', '-R', + '-p{}'.format(diff_patch._last_level), + cwd=root_path) + return_patch_run.write(patch_text) + output = return_patch_run.read() + + if return_patch_run.success(): + print('[*] Changes was returned...') + else: + print('[!] Changes was not returned...') + else: + test_result = False + assert test_result + + def test_if_diff_patch_used_for_patching_of_directories__it_changes_files_in_directories_and_adds_ones(self): + test_result = True + root_path = path.join(os.getcwd(), + 'tests/format/testfiles/a1') + patch_path = path.join(os.getcwd(), + 'tests/format/testfiles/diff_2.patch') + with open(path.join(patch_path)) as patch_file: + patch_text = patch_file.read() + + diff_patch = DiffFormat(patch_text) + print('Path:', root_path) + output = diff_patch.execute_format(root_path=root_path) + print('Output:') + print(output) + if output: + print('Changed files:') + for changed_file in diff_patch._changed_files_list: + print(changed_file, ':', sep='') + file_path = path.join(diff_patch._root_path, changed_file) + with open(file_path) as patched_file: + patched_file_text = patched_file.read() + print(patched_file_text) + other_file_path = os.path.join(path.dirname(root_path), + 'b1', changed_file) + with open(other_file_path) as other_file: + other_file_text = other_file.read() + test_result = test_result and (other_file_text == patched_file_text) + if not test_result: + print('Differences:') + try: + diff_process = Process('diff', '-u', + file_path, + other_file_path) + diff_result = diff_process.read() + print(diff_result) + except Exception as error: + print('diff was not executed.') + print('Reason:', str(error)) + + reverse_patch_run = Process('patch', '-R', + '-p{}'.format(diff_patch._last_level), + cwd=root_path) + reverse_patch_run.write(patch_text) + output = reverse_patch_run.read() + + if reverse_patch_run.success(): + print('[*] Changes was returned...') + else: + print('[!] Changes was not returned...') + else: + test_result = False + assert test_result diff --git a/tests/format/test_dovecot.py b/tests/format/test_dovecot.py new file mode 100644 index 0000000..cb709dd --- /dev/null +++ b/tests/format/test_dovecot.py @@ -0,0 +1,255 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.dovecot_format import DovecotFormat + + +@pytest.mark.dovecot +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = '''auth_default_realm = domain.com + auth_mechanisms = plain login + + !include conf.d/imap.conf + !include_try passwords.conf + + auth_realms = domain.com domain2.com + ''' + + result = OrderedDict({('', 'auth_default_realm'): + ['domain.com'], + ('', 'auth_mechanisms'): + ['plain login'], + ('', '!include', 'conf.d/imap.conf'): + [''], + ('', '!include_try', 'passwords.conf'): + [''], + ('', 'auth_realms'): + ['domain.com domain2.com'] + }) + + dovecot_object = DovecotFormat(document_text) + assert dovecot_object._document_dictionary == result + + def test_if_input_document_contains_some_block_of_parameters__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' + section optional_name { + section_setting_key = section_setting_value + subsection optional_subname { + subkey = subvalue + } + } + + local 127.0.0.2 { + key = 127.0.0.2 + } + ''' + subsection = OrderedDict({('', 'subkey'): ['subvalue']}) + section = OrderedDict({('', 'section_setting_key'): + ['section_setting_value'], + ('', 'subsection', 'optional_subname'): + subsection}) + local = OrderedDict({('', 'key'): ['127.0.0.2']}) + result = OrderedDict({('', 'section', 'optional_name'): + section, + ('', 'local', '127.0.0.2'): + local}) + + dovecot_object = DovecotFormat(document_text) + assert dovecot_object._document_dictionary == result + + def test_if_input_document_contains_some_blocks_with_similar_names__the_blocks_join_recursively(self): + document_text = ''' + section optional_name { + section_setting_key = section_setting_value + subsection optional_subname { + subkey = subvalue + } + } + + local 127.0.0.2 { + key = 127.0.0.2 + } + + section optional_name { + subsection optional_subname { + otherkey = value + } + } + + local 127.0.0.2 { + key = 127.0.0.2 + subsect name { + param = no + } + } + ''' + + subsection = OrderedDict({('', 'subkey'): ['subvalue'], + ('', 'otherkey'): ['value']}) + section = OrderedDict({('', 'section_setting_key'): + ['section_setting_value'], + ('', 'subsection', 'optional_subname'): + subsection}) + sub_sect = OrderedDict({('', 'param'): ['no']}) + local = OrderedDict({('', 'key'): ['127.0.0.2'], + ('', 'subsect', 'name'): sub_sect}) + result = OrderedDict({('', 'section', 'optional_name'): + section, + ('', 'local', '127.0.0.2'): + local}) + + dovecot_object = DovecotFormat(document_text) + assert dovecot_object._document_dictionary == result + + def test_if_input_document_contains_blocks_and_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = ''' + !auth_mechanisms = plain login + + !!include conf.d/imap.conf + + section optional_name { + !section_setting_key = section_setting_value + -subsection optional_subname { + subkey = subvalue + } + } + + !local 127.0.0.2 { + key = 127.0.0.2 + } + ''' + + subsection = OrderedDict({('', 'subkey'): ['subvalue']}) + section = OrderedDict({('!', 'section_setting_key'): + ['section_setting_value'], + ('-', 'subsection', 'optional_subname'): + subsection}) + local = OrderedDict({('', 'key'): ['127.0.0.2']}) + result = OrderedDict({('!', 'auth_mechanisms'): + ['plain login'], + ('!', '!include', 'conf.d/imap.conf'): + [''], + ('', 'section', 'optional_name'): + section, + ('!', 'local', '127.0.0.2'): + local}) + + dovecot_object = DovecotFormat(document_text) + assert dovecot_object._document_dictionary == result + + def test_if_parameters_and_blocks_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value_or_with_special_key_in_block_dictionary(self): + document_text = ''' + # Comment 1 + auth_mechanisms = plain login + + # Comment 2 + !include conf.d/imap.conf + + # Comment 3 + section optional_name { + section_setting_key = section_setting_value + # Comment in the block. + subsection optional_subname { + # Comment + subkey = subvalue + } + } + ''' + + subsection = OrderedDict({'#': ['# Comment in the block.'], + ('', 'subkey'): ['# Comment', 'subvalue']}) + section = OrderedDict({'#': ['# Comment 3'], + ('', 'section_setting_key'): + ['section_setting_value'], + ('', 'subsection', 'optional_subname'): + subsection}) + result = OrderedDict({('', 'auth_mechanisms'): + ['# Comment 1', 'plain login'], + ('', '!include', 'conf.d/imap.conf'): + ['# Comment 2', ''], + ('', 'section', 'optional_name'): + section}) + + dovecot_object = DovecotFormat(document_text) + assert dovecot_object._document_dictionary == result + + def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = ''' + # Comment 1 + auth_mechanisms = plain login + + # Comment 2 + !include conf.d/imap.conf + + # Comment 3 + section optional_name { + section_setting_key = section_setting_value + # Comment in the block. + subsection optional_subname { + # Comment + subkey = subvalue + } + } + ''' + + subsection = OrderedDict({('', 'subkey'): ['subvalue']}) + section = OrderedDict({('', 'section_setting_key'): + ['section_setting_value'], + ('', 'subsection', 'optional_subname'): + subsection}) + result = OrderedDict({('', 'auth_mechanisms'): + ['plain login'], + ('', '!include', 'conf.d/imap.conf'): + [''], + ('', 'section', 'optional_name'): + section}) + + dovecot_object = DovecotFormat(document_text, ignore_comments=True) + assert dovecot_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_values_or_with_empty_block__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = ''' + !auth_mechanisms + !auth_realms = + + section optional_name { + !section_setting_key + !subsection optional_subname { + } + } + + !local 127.0.0.2 { + } + ''' + + section = OrderedDict({('!', 'section_setting_key'): [''], + ('!', 'subsection', 'optional_subname'): + OrderedDict()}) + result = OrderedDict({('!', 'auth_mechanisms'): + [''], + ('!', 'auth_realms'): + [''], + ('', 'section', 'optional_name'): + section, + ('!', 'local', '127.0.0.2'): + OrderedDict()}) + + dovecot_object = DovecotFormat(document_text) + assert dovecot_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/dovecot_original.conf', 'r') as original_file: + original_text = original_file.read() + dovecot_original_object = DovecotFormat(original_text) + + with open('./tests/format/testfiles/dovecot_template.conf', 'r') as template_file: + template_text = template_file.read() + dovecot_template_object = DovecotFormat(template_text, + ignore_comments=True) + + dovecot_original_object.join_template(dovecot_template_object) + + with open('./tests/format/testfiles/dovecot_result.conf', 'r') as result_file: + result_text = result_file.read() + + assert dovecot_original_object.get_document_text() == result_text diff --git a/tests/format/test_json.py b/tests/format/test_json.py new file mode 100644 index 0000000..ae23989 --- /dev/null +++ b/tests/format/test_json.py @@ -0,0 +1,44 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.json_format import JSONFormat + + +@pytest.mark.json +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameters_and_parameter_blocks__the_initialised_object_contains_correct_dictionary(self): + documentText = ''' + { + "Param1":"ParamValue1", + "Param2": 1, + "BlockParam1":{ + "BlockParam1":1, + "BlockParam2":0 + }, + "Param3": true + } + ''' + + blockContent = OrderedDict({"BlockParam1": 1, "BlockParam2": 0}) + result = OrderedDict({'Param1': 'ParamValue1', + 'Param2': 1, + 'BlockParam1': blockContent, + 'Param3': True}) + + jsonObject = JSONFormat(documentText) + assert jsonObject._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/json_original.json', 'r') as originalFile: + originalText = originalFile.read() + jsonOriginalObject = JSONFormat(originalText) + + with open('./tests/format/testfiles/json_template.json', 'r') as templateFile: + templateText = templateFile.read() + jsonTemplateObject = JSONFormat(templateText) + + jsonOriginalObject.join_template(jsonTemplateObject) + + with open('./tests/format/testfiles/json_result.json', 'r') as resultFile: + resultText = resultFile.read() + + assert jsonOriginalObject.get_document_text() == resultText diff --git a/tests/format/test_kde.py b/tests/format/test_kde.py new file mode 100644 index 0000000..a2b8636 --- /dev/null +++ b/tests/format/test_kde.py @@ -0,0 +1,222 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.kde_format import KDEFormat + + +@pytest.mark.kde +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = '''[section name][first][second] + parameter name = /home/divanov/Home + other parameter = yes''' + + param_line_1 = OrderedDict({('', 'parameter name'): ['/home/divanov/Home']}) + param_line_2 = OrderedDict({('', 'other parameter'): ['yes']}) + + result = OrderedDict({('', 'section name', 'first', 'second'): + OrderedDict(**param_line_1, + **param_line_2)}) + + kde_object = KDEFormat(document_text) + assert kde_object._document_dictionary == result + + def test_if_input_document_contains_parameters_with_values_with_unicode_symbols__the_initialized_object_contains_correct_dictionary(self): + document_text = ''' + [Desktop Entry][val] + GenericName=IRC Client + GenericName[ar]=عميل IRC + GenericName[be]=Кліент IRC + GenericName[bg]=IRC клиент + GenericName[bs]=IRC klijent + GenericName[ca]=Client d'IRC + GenericName[ca@valencia]=Client d'IRC''' + + section = OrderedDict({('', 'GenericName'): ['IRC Client'], + ('', 'GenericName[ar]'): ['عميل IRC'], + ('', 'GenericName[be]'): ['Кліент IRC'], + ('', 'GenericName[bg]'): ['IRC клиент'], + ('', 'GenericName[bs]'): ['IRC klijent'], + ('', 'GenericName[ca]'): ["Client d'IRC"], + ('', 'GenericName[ca@valencia]'): ["Client d'IRC"]}) + + result = OrderedDict({('', 'Desktop Entry', 'val'): section}) + + kde_object = KDEFormat(document_text) + assert kde_object._document_dictionary == result + + def test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary(self): + document_text = ''' + [PlasmaViews][Panel 69][Horizontal2048] + alignment=132 + + length=674 + + + thickness = 56 + + [Desktop Entry] + + Exec=konversation -qwindowtitle %c %u + ''' + + section1Content = OrderedDict({('', 'alignment'): ['132'], + ('', 'length'): ['674'], + ('', 'thickness'): ['56']}) + + section_2_content = OrderedDict({('', 'Exec'): ['konversation -qwindowtitle %c %u']}) + + result = OrderedDict({('', 'PlasmaViews', 'Panel 69', 'Horizontal2048'): section1Content, + ('', 'Desktop Entry'): section_2_content}) + + kde_object = KDEFormat(document_text) + assert kde_object._document_dictionary == result + + def test_if_input_document_contains_sections_with_different_names_but_different_parameters__the_parameters_merged_in_one_section(self): + document_text = ''' + [PlasmaViews][Panel 69][Horizontal2048] + alignment=132 + + length=674 + + [PlasmaViews][Panel 69][Horizontal2048] + + thickness = 56 + + ''' + + section1Content = OrderedDict({('', 'alignment'): ['132'], + ('', 'length'): ['674'], + ('', 'thickness'): ['56']}) + + result = OrderedDict({('', 'PlasmaViews', 'Panel 69', 'Horizontal2048'): section1Content}) + + kde_object = KDEFormat(document_text) + assert kde_object._document_dictionary == result + + def test_if_input_document_contains_sections_with_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = ''' + [PlasmaViews][Panel 69][Horizontal2048] + !alignment=132 + length=674 + -thickness = 56 + ''' + + section1Content = OrderedDict({('!', 'alignment'): ['132'], + ('', 'length'): ['674'], + ('-', 'thickness'): ['56']}) + + result = OrderedDict({('', 'PlasmaViews', 'Panel 69', 'Horizontal2048'): section1Content}) + + kde_object = KDEFormat(document_text) + assert kde_object._document_dictionary == result + + def test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value(self): + document_text = ''' + # Comment to section + [PlasmaViews][Panel 69][Horizontal2048] + # Comment 1 + alignment=132 + length=674 + + # Comment 2 + #Comment 3 + thickness = 56 + + [Desktop Entry] + # Comment + + Exec=konversation -qwindowtitle %c %u + ''' + + section_1_content = OrderedDict({'#': ['# Comment to section'], + ('', 'alignment'): ['# Comment 1', + '132'], + ('', 'length'): ['674'], + ('', 'thickness'): ['# Comment 2', + '#Comment 3', + '56']}) + + section_2_content = OrderedDict({('', 'Exec'): ['# Comment', + 'konversation -qwindowtitle %c %u']}) + + result = OrderedDict({('', 'PlasmaViews', 'Panel 69', 'Horizontal2048'): section_1_content, + ('', 'Desktop Entry'): section_2_content}) + + kde_object = KDEFormat(document_text) + assert kde_object._document_dictionary == result + + def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = ''' + # Comment to section + [PlasmaViews][Panel 69][Horizontal2048] + # Comment 1 + alignment=132 + length=674 + + # Comment 2 + #Comment 3 + thickness = 56 + + [Desktop Entry] + # Comment + + Exec=konversation -qwindowtitle %c %u + ''' + + section_1_content = OrderedDict({('', 'alignment'): ['132'], + ('', 'length'): ['674'], + ('', 'thickness'): ['56']}) + + section_2_content = OrderedDict({('', 'Exec'): ['konversation -qwindowtitle %c %u']}) + + result = OrderedDict({('', 'PlasmaViews', 'Panel 69', 'Horizontal2048'): section_1_content, + ('', 'Desktop Entry'): section_2_content}) + + kde_object = KDEFormat(document_text, ignore_comments=True) + assert kde_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values_and_sections_to_delete__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = ''' + [PlasmaViews][Panel 69][Horizontal2048] + alignment=132 + !length = + !thickness + [!PlasmaViews][Panel 69] + alignment=132 + panelVisibility=1 + [-Desktop Entry] + Exec=konversation -qwindowtitle %c %u + ''' + + section_1_content = OrderedDict({('', 'alignment'): ['132'], + ('!', 'length'): [], + ('!', 'thickness'): []}) + + section_2_content = OrderedDict({('', 'alignment'): ['132'], + ('', 'panelVisibility'): ['1']}) + + section3Content = OrderedDict({('', 'Exec'): ['konversation -qwindowtitle %c %u']}) + + result = OrderedDict({('', 'PlasmaViews', 'Panel 69', 'Horizontal2048'): section_1_content, + ('!', 'PlasmaViews', 'Panel 69'): section_2_content, + ('-', 'Desktop Entry'): section3Content}) + + kde_object = KDEFormat(document_text) + assert kde_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/kde_original', 'r') as original_file: + original_text = original_file.read() + kde_original_object = KDEFormat(original_text) + + with open('./tests/format/testfiles/kde_template', 'r') as template_file: + template_text = template_file.read() + kde_template_object = KDEFormat(template_text, + ignore_comments=True) + + kde_original_object.join_template(kde_template_object) + + with open('./tests/format/testfiles/kde_result', 'r') as result_file: + result_text = result_file.read() + + assert kde_original_object.get_document_text() == result_text diff --git a/tests/format/test_kernel.py b/tests/format/test_kernel.py new file mode 100644 index 0000000..bab9211 --- /dev/null +++ b/tests/format/test_kernel.py @@ -0,0 +1,112 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.kernel_format import KernelFormat + + +@pytest.mark.kernel +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' + CONFIG_CC_IS_GCC=y + CONFIG_GCC_VERSION=90200 + ''' + + result = OrderedDict({('', 'CONFIG_CC_IS_GCC'): ['y'], + ('', 'CONFIG_GCC_VERSION'): ['90200']}) + + kernel_object = KernelFormat(document_text) + assert kernel_object._document_dictionary == result + + def test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary(self): + document_text = ''' + CONFIG_CLANG_VERSION=0 + + + + + CONFIG_CC_HAS_ASM_GOTO=y + + + CONFIG_IRQ_WORK=y + ''' + + result = OrderedDict({('', 'CONFIG_CLANG_VERSION'): ['0'], + ('', 'CONFIG_CC_HAS_ASM_GOTO'): ['y'], + ('', 'CONFIG_IRQ_WORK'): ['y']}) + + kernel_object = KernelFormat(document_text) + assert kernel_object._document_dictionary == result + + def test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = ''' + -CONFIG_REISERFS_FS=y + CONFIG_CC_IS_GCC=y + !CONFIG_IRQ_WORK=y + ''' + + result = OrderedDict({('-', 'CONFIG_REISERFS_FS'): ['y'], + ('', 'CONFIG_CC_IS_GCC'): ['y'], + ('!', 'CONFIG_IRQ_WORK'): ['y']}) + + kernel_object = KernelFormat(document_text) + assert kernel_object._document_dictionary == result + + def test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value(self): + document_text = '''# Comment1 + CONFIG_DEFAULT_HOSTNAME="calculate" + + CONFIG_SWAP=y + + + # Comment2 + # Comment3 + CONFIG_SYSVIPC=y + CONFIG_SYSVIPC_SYSCTL=y + + # Очень важный комментарий, который нужно удалить. + !CONFIG_CROSS_MEMORY_ATTACH=y + ''' + + result = OrderedDict({('', 'CONFIG_DEFAULT_HOSTNAME'): + ['# Comment1', '"calculate"'], + ('', 'CONFIG_SWAP'): + ['y'], + ('', 'CONFIG_SYSVIPC'): + ['# Comment2', '# Comment3', 'y'], + ('', 'CONFIG_SYSVIPC_SYSCTL'): ['y'], + ('!', 'CONFIG_CROSS_MEMORY_ATTACH'): + ['# Очень важный комментарий, который нужно удалить.','y']}) + + kernel_object = KernelFormat(document_text) + assert kernel_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = ''' + !CONFIG_REISERFS_FS=y + !CONFIG_EXT3_FS_POSIX_ACL= + !CONFIG_EXT3_FS_SECURITY + ''' + + result = OrderedDict({('!', 'CONFIG_REISERFS_FS'): ['y'], + ('!', 'CONFIG_EXT3_FS_POSIX_ACL'): [], + ('!', 'CONFIG_EXT3_FS_SECURITY'): []}) + + kernel_object = KernelFormat(document_text) + assert kernel_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/kernel_original', 'r') as original_file: + original_text = original_file.read() + kernel_original_object = KernelFormat(original_text) + + with open('./tests/format/testfiles/kernel_template', 'r') as template_file: + template_text = template_file.read() + kernel_template_object = KernelFormat(template_text, + ignore_comments=True) + + kernel_original_object.join_template(kernel_template_object) + + with open('./tests/format/testfiles/kernel_result', 'r') as result_file: + result_text = result_file.read() + + assert kernel_original_object.get_document_text() == result_text diff --git a/tests/format/test_ldap.py b/tests/format/test_ldap.py new file mode 100644 index 0000000..93e68d7 --- /dev/null +++ b/tests/format/test_ldap.py @@ -0,0 +1,364 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.ldap_format import LDAPFormat + + +@pytest.mark.ldap +class TestParsingMethods: + def test_if_logiclines_method_takes_text_with_lines_that_starts_whit_space_symbols__it_returns_joined_lines(self): + with open('./tests/format/testfiles/ldap_logic_lines_test.txt', 'r') as input_file: + input_text = input_file.read() + + output_lines = ['First string of test file.', + 'Second string of test file.', + 'Third string of test file.', + 'Fourth string of test file.'] + + ldap_object = LDAPFormat('') + input_lines = ldap_object._get_list_of_logic_lines(input_text) + assert input_lines == output_lines + + def test_if_input_doc_contains_some_type_sections_with_plain_directives__object_dictionary_contains_correct_dictionary_with_directives_values_and_comments(self): + document_text = ''' +include /etc/openldap/schema/samba.schema +include /etc/openldap/schema/mail.schema + +loglevel 0 +allow bind_v2 +modulepath /usr/lib/openldap/openldap + +database bdb + +checkpoint 1024 5 +suffix "dc=calculate" +cachesize 10000 +sizelimit unlimited +directory /var/lib/openldap-data +rootdn "cn=ldaproot,dc=calculate"''' + + global_section = OrderedDict({'#': [], + ('', 'include', '/etc/openldap/schema/samba.schema'): + [''], + ('', 'include', '/etc/openldap/schema/mail.schema'): + [''], + ('', 'loglevel'): ['0'], + ('', 'allow'): ['bind_v2'], + ('', 'modulepath'): + ['/usr/lib/openldap/openldap']}) + + database_section = OrderedDict({('', 'checkpoint'): ['1024 5'], + ('', 'suffix'): ['"dc=calculate"'], + ('', 'cachesize'): ['10000'], + ('', 'sizelimit'): ['unlimited'], + ('', 'directory'): + ['/var/lib/openldap-data'], + ('', 'rootdn'): + ['"cn=ldaproot,dc=calculate"']}) + + result = OrderedDict({('', 'global'): global_section, + ('', 'database', 'bdb'): database_section}) + + ldap_object = LDAPFormat(document_text) + assert ldap_object._document_dictionary == result + + def test_if_input_document_contains_access_to_directive__the_object_s_dictionary_contains_correct_dictionary_with_list_of_access_to_parameters_and_comments(self): + document_text = ''' +database bdb +# Comment1 +# Comment2 +access to attrs=userPassword + by dn="cn=ldapadmin,dc=calculate" write + by dn="ou=Samba,ou=Services,dc=calculate" write + by self read + by * auth + +# Comment3 +access to * by * read + ''' + access_1 = OrderedDict({'#': ['# Comment1', '# Comment2'], + ('', '*'): + ['auth'], + ('', 'self'): + ['read'], + ('', 'dn="ou=Samba,ou=Services,dc=calculate"'): + ['write'], + ('', 'dn="cn=ldapadmin,dc=calculate"'): + ['write']}) + + access_2 = OrderedDict({'#': ['# Comment3'], + ('', '*'): ['read']}) + + database_section = OrderedDict({('', 'access to', 'attrs=userPassword'): + access_1, + ('', 'access to', '*'): + access_2}) + + result = OrderedDict({('', 'global'): OrderedDict(), + ('', 'database', 'bdb'): database_section}) + + ldap_object = LDAPFormat(document_text) + assert ldap_object._document_dictionary == result + + def test_if_input_document_contains_syncrepl_directive__the_object_s_dictionary_contains_correct_dictionary_with_list_of_syncrepl_parameters_and_comments(self): + document_text = ''' +# Comment1 +# Comment2 +syncrepl rid=123 + provider=ldap://provider.example.com:389 + type=refreshOnly + interval=01:00:00:00 + searchbase="dc=example,dc=com" + ''' + + syncrepl_dictionary = OrderedDict({('', 'provider'): + ['ldap://provider.example.com:389'], + ('', 'type'): + ['refreshOnly'], + ('', 'interval'): + ['01:00:00:00'], + ('', 'searchbase'): + ['"dc=example,dc=com"']}) + + global_section = OrderedDict({'#': ['# Comment1', '# Comment2'], + ('', 'syncrepl', 'rid=123'): + syncrepl_dictionary}) + + result = OrderedDict({('', 'global'): global_section}) + + ldap_object = LDAPFormat(document_text) + assert ldap_object._document_dictionary == result + + def test_if_input_document_contains_index_directives__the_object_s_dictionary_contains_correct_dictionary_with_index_elements_and_comments(self): + document_text = ''' +# Comment for global + +index objectClass eq +# Comment for index +index cn pres,sub,eq +index sn pres,sub,eq +index uid pres,sub,eq + +database bdb +# Comment1 +# Comment2 +index uidNumber eq +index gidNumber eq +index default sub''' + + global_section = OrderedDict({'#': ['# Comment for global'], + ('', 'index', 'objectClass'): ['eq'], + ('', 'index', 'cn'): + ['# Comment for index', 'pres,sub,eq'], + ('', 'index', 'sn'): ['pres,sub,eq'], + ('', 'index', 'uid'): ['pres,sub,eq']}) + + database_section = OrderedDict({('', 'index', 'uidNumber'): + ['# Comment1', '# Comment2', 'eq'], + ('', 'index', 'gidNumber'): ['eq'], + ('', 'index', 'default'): ['sub']}) + + result = OrderedDict({('', 'global'): global_section, + ('', 'database', 'bdb'): database_section}) + + ldap_object = LDAPFormat(document_text) + assert ldap_object._document_dictionary == result + + def test_if_input_document_contains_comments_to_type_sections__the_object_s_dictionary_collect_them(self): + document_text = ''' +# Comment1 +loglevel 0 +allow bind_v2 +modulepath /usr/lib/openldap/openldap + +# Comment2 +database bdb +# Comment3 +checkpoint 1024 5 +suffix "dc=calculate" +cachesize 10000''' + + global_section = OrderedDict({'#': ['# Comment1'], + ('', 'loglevel'): ['0'], + ('', 'allow'): ['bind_v2'], + ('', 'modulepath'): + ['/usr/lib/openldap/openldap']}) + + database_section = OrderedDict({'#': ['# Comment2'], + ('', 'checkpoint'): + ['# Comment3', + '1024 5'], + ('', 'suffix'): ['"dc=calculate"'], + ('', 'cachesize'): ['10000']}) + + result = OrderedDict({('', 'global'): global_section, + ('', 'database', 'bdb'): database_section}) + + ldap_object = LDAPFormat(document_text) + assert ldap_object._document_dictionary == result + + def test_if_input_doc_contains_some_type_sections_with_plain_directives_and_action_marks__object_dictionary_contains_correct_dictionary_with_directives_values_and_comments(self): + document_text = ''' +!include /etc/openldap/schema/samba.schema +-include /etc/openldap/schema/mail.schema + +-backend hdb +modulepath /usr/lib/openldap/openldap + +!database bdb + +checkpoint 1024 5 +!directory /var/lib/openldap-data +!rootdn +-allow + +# Comment1 +!index uidNumber eq +-index gidNumber eq + +# Comment for index +!index cn +!index sn + +''' + + global_section = OrderedDict({'#': [], + ('!', 'include', '/etc/openldap/schema/samba.schema'): + [''], + ('-', 'include', '/etc/openldap/schema/mail.schema'): + ['']}) + + backend_section = OrderedDict({('', 'modulepath'): + ['/usr/lib/openldap/openldap']}) + + database_section = OrderedDict({('', 'checkpoint'): ['1024 5'], + ('!', 'directory'): + ['/var/lib/openldap-data'], + ('!', 'rootdn'): [], + ('!', 'index', 'uidNumber'): + ['# Comment1', 'eq'], + ('-', 'index', 'gidNumber'): ['eq'], + ('!', 'index', 'cn'): + ['# Comment for index'], + ('!', 'index', 'sn'): + []}) + + result = OrderedDict({('', 'global'): global_section, + ('-', 'backend', 'hdb'): backend_section, + ('!', 'database', 'bdb'): database_section}) + + ldap_object = LDAPFormat(document_text) + assert ldap_object._document_dictionary == result + + def test_if_template_text_contains_some_access_to_constuctions_with_same_what_value_and_without_action_marks_for_whole_constructions__they_join_in_one_access_to_construction(self): + document_text = ''' +# Unix +access to attrs=sambaLMPassword,sambaNTPassword + by dn="ou=Unix,ou=Services,dc=calculate" write + +# Newval +access to attrs=sambaLMPassword,sambaNTPassword + by dn="cn=newval, dc=calculate" read + +# Mail +access to attrs=sambaLMPassword,sambaNTPassword + by dn="ou=Mail,ou=Services,dc=calculate" read + +# Jabber +access to attrs=sambaLMPassword,sambaNTPassword + by dn="ou=Jabber,ou=Services,dc=calculate" read + ''' + + access = OrderedDict({('', 'dn="ou=Unix,ou=Services,dc=calculate"'): + ['write'], + ('', 'dn="cn=newval, dc=calculate"'): + ['read'], + ('', 'dn="ou=Mail,ou=Services,dc=calculate"'): + ['read'], + ('', 'dn="ou=Jabber,ou=Services,dc=calculate"'): + ['read']}) + + global_section = OrderedDict({('', 'access to', + 'attrs=sambaLMPassword,sambaNTPassword'): + access}) + + result = OrderedDict({('', 'global'): global_section}) + + ldap_object = LDAPFormat(document_text, ignore_comments=True) + assert ldap_object._document_dictionary == result + + def test_if_input_document_contains_syncrepl_and_access_to_constructions_with_action_marks__object_dictionary_contains_correct_dictionary_with_action_marks(self): + document_text = ''' +# Comment +!syncrepl rid=123 + provider=ldap://provider.example.com:389 + type=refreshOnly + interval=01:00:00:00 + searchbase="dc=example,dc=com" + +# Comment1 +!syncrepl rid=001 + +database bdb +# Comment2 +!access to attrs=userPassword + by dn="cn=ldapadmin,dc=calculate" write + by !dn="ou=Samba,ou=Services,dc=calculate" write + by self read + by * auth + +# Comment3 +!access to dn.base="cn=proxyuser,dc=calculate" +''' + + access_1 = OrderedDict({'#': ['# Comment2'], + ('', '*'): + ['auth'], + ('', 'self'): + ['read'], + ('!', 'dn="ou=Samba,ou=Services,dc=calculate"'): + ['write'], + ('', 'dn="cn=ldapadmin,dc=calculate"'): + ['write']}) + + syncrepl_dictionary = OrderedDict({('', 'provider'): + ['ldap://provider.example.com:389'], + ('', 'type'): + ['refreshOnly'], + ('', 'interval'): + ['01:00:00:00'], + ('', 'searchbase'): + ['"dc=example,dc=com"']}) + + global_section = OrderedDict({'#': ['# Comment'], + ('!', 'syncrepl', 'rid=123'): + syncrepl_dictionary, + ('!', 'syncrepl', 'rid=001'): + OrderedDict({'#': ['# Comment1']})}) + + database_section = OrderedDict({('!', 'access to', 'attrs=userPassword'): + access_1, + ('!', 'access to', 'dn.base="cn=proxyuser,dc=calculate"'): + OrderedDict({'#': ['# Comment3']})}) + + result = OrderedDict({('', 'global'): global_section, + ('', 'database', 'bdb'): database_section}) + + ldap_object = LDAPFormat(document_text) + assert ldap_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/ldap_original.conf', 'r') as original_file: + original_text = original_file.read() + ldap_original_object = LDAPFormat(original_text) + + with open('./tests/format/testfiles/ldap_template.conf', 'r') as template_file: + template_text = template_file.read() + ldap_template_object = LDAPFormat(template_text, + ignore_comments=True) + + ldap_original_object.join_template(ldap_template_object) + + with open('./tests/format/testfiles/ldap_result.conf', 'r') as result_file: + result_text = result_file.read() + + assert ldap_original_object.get_document_text() == result_text diff --git a/tests/format/test_openrc.py b/tests/format/test_openrc.py new file mode 100644 index 0000000..87127c1 --- /dev/null +++ b/tests/format/test_openrc.py @@ -0,0 +1,146 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.openrc_format import OpenRCFormat + + +@pytest.mark.openrc +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' + rc_interactive="NO" + INSTANCE="openldap${SVCNAME#slapd}" + OPTS_CONF="-f /etc/${INSTANCE}/slapd.conf" + OPTS="${OPTS_CONF} -h 'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock'" + ''' + + result = OrderedDict({('', 'rc_interactive'): ['"NO"'], + ('', 'instance'): ['"openldap${SVCNAME#slapd}"'], + ('', 'opts_conf'): ['"-f /etc/${INSTANCE}/slapd.conf"'], + ('', 'opts'): ['"${OPTS_CONF} -h \'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock\'"']}) + + openrc_object = OpenRCFormat(document_text) + assert openrc_object._document_dictionary == result + + def test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary(self): + document_text = ''' + rc_interactive="NO" + + + INSTANCE="openldap${SVCNAME#slapd}" + + OPTS_CONF="-f /etc/${INSTANCE}/slapd.conf" + + + OPTS="${OPTS_CONF} -h 'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock'" + ''' + + result = OrderedDict({('', 'rc_interactive'): ['"NO"'], + ('', 'instance'): ['"openldap${SVCNAME#slapd}"'], + ('', 'opts_conf'): ['"-f /etc/${INSTANCE}/slapd.conf"'], + ('', 'opts'): ['"${OPTS_CONF} -h \'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock\'"']}) + + openrc_object = OpenRCFormat(document_text) + assert openrc_object._document_dictionary == result + + def test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = ''' + !rc_interactive="NO" + -INSTANCE="openldap${SVCNAME#slapd}" + OPTS_CONF="-f /etc/${INSTANCE}/slapd.conf" + !OPTS="${OPTS_CONF} -h 'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock'" + ''' + + result = OrderedDict({('!', 'rc_interactive'): ['"NO"'], + ('-', 'instance'): ['"openldap${SVCNAME#slapd}"'], + ('', 'opts_conf'): ['"-f /etc/${INSTANCE}/slapd.conf"'], + ('!', 'opts'): ['"${OPTS_CONF} -h \'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock\'"']}) + + openrc_object = OpenRCFormat(document_text) + assert openrc_object._document_dictionary == result + + def test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value(self): + document_text = ''' + # If you have multiple slapd instances per #376699, this will provide a default config + rc_interactive="NO" + INSTANCE="openldap${SVCNAME#slapd}" + + # Comment1 + # Comment2 + + OPTS_CONF="-f /etc/${INSTANCE}/slapd.conf" + + + OPTS="${OPTS_CONF} -h 'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock'" + ''' + + result = OrderedDict({('', 'rc_interactive'): + ['# If you have multiple slapd instances per #376699, this will provide a default config', '"NO"'], + ('', 'instance'): + ['"openldap${SVCNAME#slapd}"'], + ('', 'opts_conf'): + ['# Comment1', + '# Comment2', + '"-f /etc/${INSTANCE}/slapd.conf"'], + ('', 'opts'): + ['"${OPTS_CONF} -h \'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock\'"']}) + + openrc_object = OpenRCFormat(document_text) + assert openrc_object._document_dictionary == result + + def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = ''' + # If you have multiple slapd instances per #376699, this will provide a default config + rc_interactive="NO" + INSTANCE="openldap${SVCNAME#slapd}" + + # Comment1 + # Comment2 + + OPTS_CONF="-f /etc/${INSTANCE}/slapd.conf" + + + OPTS="${OPTS_CONF} -h 'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock'" + ''' + + result = OrderedDict({('', 'rc_interactive'): + ['"NO"'], + ('', 'instance'): + ['"openldap${SVCNAME#slapd}"'], + ('', 'opts_conf'): + ['"-f /etc/${INSTANCE}/slapd.conf"'], + ('', 'opts'): + ['"${OPTS_CONF} -h \'ldaps:// ldap:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock\'"']}) + + openrc_object = OpenRCFormat(document_text, ignore_comments=True) + assert openrc_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = ''' + !rc_interactive="NO" + !INSTANCE= + !OPTS_CONF + ''' + + result = OrderedDict({('!', 'rc_interactive'): ['"NO"'], + ('!', 'instance'): [], + ('!', 'opts_conf'): []}) + + openrc_object = OpenRCFormat(document_text) + assert openrc_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/openrc_original', 'r') as original_file: + original_text = original_file.read() + openrc_original_object = OpenRCFormat(original_text) + + with open('./tests/format/testfiles/openrc_template', 'r') as template_file: + template_text = template_file.read() + openrc_template_object = OpenRCFormat(template_text, + ignore_comments=True) + + openrc_original_object.join_template(openrc_template_object) + + with open('./tests/format/testfiles/openrc_result', 'r') as result_file: + result_text = result_file.read() + + assert openrc_original_object.get_document_text() == result_text diff --git a/tests/format/test_patch.py b/tests/format/test_patch.py new file mode 100644 index 0000000..f1693ee --- /dev/null +++ b/tests/format/test_patch.py @@ -0,0 +1,80 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.patch_format import PatchFormat + + +@pytest.mark.patch +class TestParsingMethods: + def test_if_input_patch_document_contains_only_regular_expressions_without_any_regex_flags__it_correctly_patches_input_document(self): + input_text = ''' +First line +Another meaningless line. +TEXT&DATA +ParameterName = Value +Another line of endless sadness. + ''' + + output_text = ''' +First line +Another meaningless line. +TEXT_CONFIG +ParameterName = NewValue +Another line of endless sadness. + ''' + + patch_text = '''TEXT&DATA + TEXT_CONFIG + ParameterName\\s*=\\s*[a-zA-Z_][a-zA-Z_0-9]* + ParameterName = NewValue + ''' + + patch = PatchFormat(patch_text) + patch_result = patch.execute_format(input_text) + + assert patch_result == output_text + + def test_if_input_patch_document_contains_regular_expressions_with_global_regex_flags_and_flags_as_attributes__it_correctly_patches_input_document_using_regex_flags(self): + input_text = ''' +#============================== Share Definitions ============================= +[homes] + comment = Home Directories + browseable = no + writable = yes + +# Un-comment the following and create the netlogon directory for Domain Logons +[netlogon] + comment = Network Logon Service + path = /var/lib/samba/netlogon + guest ok = yes + writable = no + share modes = no + ''' + + output_text = ''' +#============================== Share Definitions ============================= +[homes] + comment = Home Directories + browseable = who knows + writable = yes + +# Un-comment the following and create the netlogon directory for Domain Logons +[netlogon] + comment = Network Logon Service + path = /var/lib/samba/netlogon + writable = yes + share modes = no + ''' + + patch_text = ''' + (\\[netlogon\\].*)writable\\s*=\\s*[a-zA-Z_][a-zA-Z_0-9]* + \\1writable = yes + (\\[homes\\].*)browseable\\s*=\\s*[a-zA-Z_][a-zA-Z_0-9]* + \\1browseable = who knows + (\\[netlogon\\].*)^\\s*guest ok\\s*=\\s*[a-zA-Z_][a-zA-Z_0-9]*\\n + \\1 + ''' + + patch = PatchFormat(patch_text, multiline=True, dotall=True) + patch_result = patch.execute_format(input_text) + + assert patch_result == output_text diff --git a/tests/format/test_postfix.py b/tests/format/test_postfix.py new file mode 100644 index 0000000..4e67d51 --- /dev/null +++ b/tests/format/test_postfix.py @@ -0,0 +1,124 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.postfix_format import PostfixFormat + + +@pytest.mark.postfix +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = '''parameter_name = /home/divanov/Home + other_parameter = yes''' + + result = OrderedDict({('', 'parameter_name'): ['/home/divanov/Home'], + ('', 'other_parameter'): ['yes']}) + + postfix_object = PostfixFormat(document_text) + assert postfix_object._document_dictionary == result + + def test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary(self): + document_text = '''parameter_name = /home/divanov/Home + + + other_parameter = yes + + another_parameter = none + ''' + + result = OrderedDict({('', 'parameter_name'): ['/home/divanov/Home'], + ('', 'other_parameter'): ['yes'], + ('', 'another_parameter'): ['none']}) + + postfix_object = PostfixFormat(document_text) + assert postfix_object._document_dictionary == result + + def test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = '''-parameter_name = /home/divanov/Home + other_parameter = yes + !another_parameter = 1''' + + result = OrderedDict({('-', 'parameter_name'): ['/home/divanov/Home'], + ('', 'other_parameter'): ['yes'], + ('!', 'another_parameter'): ['1']}) + + postfix_object = PostfixFormat(document_text) + assert postfix_object._document_dictionary == result + + def test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value(self): + document_text = '''# Comment1 + parameter_name = /home/divanov/Home + + # Comment2 + # Comment3 + other_parameter = yes + smtp_tls_note_starttls_offer = yes + tls_random_source = dev:/dev/urandom + + # Очень важный комментарий, который нужно удалить. + !another_parameter = 1''' + + result = OrderedDict({('', 'parameter_name'): + ['# Comment1', '/home/divanov/Home'], + ('', 'other_parameter'): + ['# Comment2', '# Comment3', 'yes'], + ('', 'smtp_tls_note_starttls_offer'): ['yes'], + ('', 'tls_random_source'): ['dev:/dev/urandom'], + ('!', 'another_parameter'): + ['# Очень важный комментарий, который нужно удалить.','1']}) + + postfix_object = PostfixFormat(document_text) + assert postfix_object._document_dictionary == result + + def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = '''# Comment1 + parameter_name = /home/divanov/Home + + # Comment2 + # Comment3 + other_parameter = yes + smtp_tls_note_starttls_offer = yes + tls_random_source = dev:/dev/urandom + + # Очень важный комментарий, который нужно удалить. + !another_parameter = 1''' + + result = OrderedDict({('', 'parameter_name'): + ['/home/divanov/Home'], + ('', 'other_parameter'): + ['yes'], + ('', 'smtp_tls_note_starttls_offer'): ['yes'], + ('', 'tls_random_source'): ['dev:/dev/urandom'], + ('!', 'another_parameter'): + ['1']}) + + postfix_object = PostfixFormat(document_text, ignore_comments=True) + assert postfix_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = '''!parameter_name = /home/divanov/Home + !other_parameter = + -ignored_line + !another_parameter''' + + result = OrderedDict({('!', 'parameter_name'): ['/home/divanov/Home'], + ('!', 'other_parameter'): [], + ('!', 'another_parameter'): []}) + + postfix_object = PostfixFormat(document_text) + assert postfix_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/postfix_original', 'r') as original_file: + original_text = original_file.read() + postfix_original_object = PostfixFormat(original_text) + + with open('./tests/format/testfiles/postfix_template', 'r') as template_file: + template_text = template_file.read() + postfix_template_object = PostfixFormat(template_text, + ignore_comments=True) + + postfix_original_object.join_template(postfix_template_object) + + with open('./tests/format/testfiles/postfix_result', 'r') as result_file: + result_text = result_file.read() + + assert postfix_original_object.get_document_text() == result_text diff --git a/tests/format/test_procmail.py b/tests/format/test_procmail.py new file mode 100644 index 0000000..576d8d4 --- /dev/null +++ b/tests/format/test_procmail.py @@ -0,0 +1,130 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.procmail_format import ProcmailFormat + + +@pytest.mark.procmail +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' + net.ipv4.ip_forward = 0 + parameter_name = /home/divanov/Home + other_parameter = yes''' + + result = OrderedDict({('', 'net.ipv4.ip_forward'): ['0'], + ('', 'parameter_name'): ['/home/divanov/Home'], + ('', 'other_parameter'): ['yes']}) + + procmail_object = ProcmailFormat(document_text) + assert procmail_object._document_dictionary == result + + def test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary(self): + document_text = ''' + net.ipv4.ip_forward = 0 + + + parameter_name = /home/divanov/Home + + other_parameter = yes''' + + result = OrderedDict({('', 'net.ipv4.ip_forward'): ['0'], + ('', 'parameter_name'): ['/home/divanov/Home'], + ('', 'other_parameter'): ['yes']}) + + procmail_object = ProcmailFormat(document_text) + assert procmail_object._document_dictionary == result + + def test_if_input_document_contains_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = ''' + !net.ipv4.ip_forward = 0 + + + local_recipient_maps = ldap:/etc/postfix/ldap-recipient.cf, ldap:/etc/postfix/ldap-recipient-gr.cf, ldap:/etc/postfix/ldap-recipient-repl.cf + parameter_name = /home/divanov/Home + + -other_parameter = yes''' + + result = OrderedDict({('!', 'net.ipv4.ip_forward'): ['0'], + ('', 'local_recipient_maps'): + ['ldap:/etc/postfix/ldap-recipient.cf, ldap:/etc/postfix/ldap-recipient-gr.cf, ldap:/etc/postfix/ldap-recipient-repl.cf'], + ('', 'parameter_name'): ['/home/divanov/Home'], + ('-', 'other_parameter'): ['yes']}) + + procmail_object = ProcmailFormat(document_text) + assert procmail_object._document_dictionary == result + + def test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value(self): + document_text = ''' + # Comment1 + net.ipv4.ip_forward = 0 + + + # Comment2 + # Comment3 + parameter_name = /home/divanov/Home + + # Comment + !other_parameter = yes''' + + result = OrderedDict({('', 'net.ipv4.ip_forward'): ['# Comment1', '0'], + ('', 'parameter_name'): ['# Comment2', + '# Comment3', + '/home/divanov/Home'], + ('!', 'other_parameter'): ['# Comment', + 'yes']}) + + procmail_object = ProcmailFormat(document_text) + assert procmail_object._document_dictionary == result + + def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = ''' + # Comment1 + net.ipv4.ip_forward = 0 + + + # Comment2 + # Comment3 + parameter_name = /home/divanov/Home + + # Comment + !other_parameter = yes''' + + result = OrderedDict({('', 'net.ipv4.ip_forward'): ['0'], + ('', 'parameter_name'): ['/home/divanov/Home'], + ('!', 'other_parameter'): ['yes']}) + + procmail_object = ProcmailFormat(document_text, ignore_comments=True) + assert procmail_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = ''' + !net.ipv4.ip_forward = 0 + + + !parameter_name = + + !other_parameter''' + + result = OrderedDict({('!', 'net.ipv4.ip_forward'): ['0'], + ('!', 'parameter_name'): [], + ('!', 'other_parameter'): []}) + + procmail_object = ProcmailFormat(document_text) + assert procmail_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/procmail_original', 'r') as original_file: + original_text = original_file.read() + procmail_original_object = ProcmailFormat(original_text) + + with open('./tests/format/testfiles/procmail_template', 'r') as template_file: + template_text = template_file.read() + procmail_template_object = ProcmailFormat(template_text, + ignore_comments=True) + + procmail_original_object.join_template(procmail_template_object) + + with open('./tests/format/testfiles/procmail_result', 'r') as result_file: + result_text = result_file.read() + + assert procmail_original_object.get_document_text() == result_text diff --git a/tests/format/test_proftpd.py b/tests/format/test_proftpd.py new file mode 100644 index 0000000..1736492 --- /dev/null +++ b/tests/format/test_proftpd.py @@ -0,0 +1,237 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.proftpd_format import ProFTPDFormat + + +@pytest.mark.proftpd +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' + ServerName "ProFTPD Anonymous Server" + ServerType standalone + DefaultServer on + MaxInstances 30 + ''' + + result = OrderedDict({('', '', 'ServerName'): + ['"ProFTPD Anonymous Server"'], + ('', '', 'ServerType'): + ['standalone'], + ('', '', 'DefaultServer'): + ['on'], + ('', '', 'MaxInstances'): + ['30']}) + + proftpd_object = ProFTPDFormat(document_text) + assert proftpd_object._document_dictionary == result + + def test_if_input_document_contains_some_block_of_parameters__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' + ServerName "ProFTPD Anonymous Server" + ServerType standalone + + + + DisplayLogin welcome.msg + DisplayFirstChdir .message + + + User bob + Group bobs + UserAlias anonymous ftp + + + Order allow,deny + Allow from 10.0.0 + Deny from all + + + ''' + + result = OrderedDict({('', '', 'ServerName'): + ['"ProFTPD Anonymous Server"'], + ('', '', 'ServerType'): + ['standalone'], + ('', (('Anonymous', '~ftp'), ('Limit', 'LOGIN')), + 'DisplayLogin'): ['welcome.msg'], + ('', (('Anonymous', '~ftp'), ('Limit', 'LOGIN')), + 'DisplayFirstChdir'): ['.message'], + ('', (('Anonymous', '~ftp'), ), + 'User'): ['bob'], + ('', (('Anonymous', '~ftp'), ), + 'Group'): ['bobs'], + ('', (('Anonymous', '~ftp'), ), + 'UserAlias', 'anonymous', 'ftp'): [''], + ('', (('Anonymous', '~ftp'), ('Limit', 'WRITE')), + 'Order'): ['allow,deny'], + ('', (('Anonymous', '~ftp'), ('Limit', 'WRITE')), + 'Allow from', '10.0.0'): [''], + ('', (('Anonymous', '~ftp'), ('Limit', 'WRITE')), + 'Deny from', 'all'): ['']}) + + proftpd_object = ProFTPDFormat(document_text) + assert proftpd_object._document_dictionary == result + + def test_if_input_document_contains_blocks_and_parameters_with_action_marks__the_key_tuples_of_parameters_s_have_it_as_its_first_element_inherited(self): + document_text = ''' + !ServerName "ProFTPD Anonymous Server" + + + <-Limit LOGIN> + DisplayLogin welcome.msg + DisplayFirstChdir .message + + + + + Order allow,deny + Allow from 10.0.0 + -Deny from all + + ''' + + result = OrderedDict({('!', '', 'ServerName'): + ['"ProFTPD Anonymous Server"'], + ('!', (('Anonymous', '~ftp'), + ('Limit', 'LOGIN')), + 'DisplayLogin'): ['welcome.msg'], + ('!', (('Anonymous', '~ftp'), + ('Limit', 'LOGIN')), + 'DisplayFirstChdir'): ['.message'], + ('!', (('Limit', 'WRITE'), ), + 'Order'): ['allow,deny'], + ('!', (('Limit', 'WRITE'), ), + 'Allow from', '10.0.0'): [''], + ('!', (('Limit', 'WRITE'), ), + 'Deny from', 'all'): ['']}) + + proftpd_object = ProFTPDFormat(document_text) + assert proftpd_object._document_dictionary == result + + def test_if_parameters_and_blocks_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value_or_with_special_key_in_block_dictionary(self): + document_text = ''' + # Comment 1 + !ServerName "ProFTPD Anonymous Server" + + # Comment 2 + # Comment 3 + + <-Limit LOGIN> + DisplayLogin welcome.msg + DisplayFirstChdir .message + + + + + Order allow,deny + Allow from 10.0.0 + # Comment 4 + -Deny from all + + ''' + + result = OrderedDict({('!', '', 'ServerName'): + ['# Comment 1', '"ProFTPD Anonymous Server"'], + ('!', (('Anonymous', '~ftp'), + ('Limit', 'LOGIN')), + 'DisplayLogin'): ['# Comment 2', + '# Comment 3', + 'welcome.msg'], + ('!', (('Anonymous', '~ftp'), + ('Limit', 'LOGIN')), + 'DisplayFirstChdir'): ['.message'], + ('!', (('Limit', 'WRITE'), ), + 'Order'): ['allow,deny'], + ('!', (('Limit', 'WRITE'), ), + 'Allow from', '10.0.0'): [''], + ('!', (('Limit', 'WRITE'), ), + 'Deny from', 'all'): ['# Comment 4', '']}) + + proftpd_object = ProFTPDFormat(document_text) + assert proftpd_object._document_dictionary == result + + def test_if_the_ignoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = ''' + # Comment 1 + ServerName "ProFTPD Anonymous Server" + + # Comment 2 + # Comment 3 + + <-Limit LOGIN> + DisplayLogin welcome.msg + DisplayFirstChdir .message + + + + + Order allow,deny + Allow from 10.0.0 + # Comment 4 + !Deny from all + + ''' + + result = OrderedDict({('', '', 'ServerName'): + ['"ProFTPD Anonymous Server"'], + ('!', (('Anonymous', '~ftp'), + ('Limit', 'LOGIN')), + 'DisplayLogin'): ['welcome.msg'], + ('!', (('Anonymous', '~ftp'), + ('Limit', 'LOGIN')), + 'DisplayFirstChdir'): ['.message'], + ('', (('Limit', 'WRITE'), ), + 'Order'): ['allow,deny'], + ('', (('Limit', 'WRITE'), ), + 'Allow from', '10.0.0'): [''], + ('!', (('Limit', 'WRITE'), ), + 'Deny from', 'all'): ['']}) + + proftpd_object = ProFTPDFormat(document_text, ignore_comments=True) + assert proftpd_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_any_values_the_document_object_contains_dictionary_with_this_items_to_delete(self): + document_text = ''' + !SQLLog PASS + + + + !DisplayLogin + DisplayFirstChdir .message + + + + + !Allow from 10.0.0 + + ''' + + result = OrderedDict({('!', '', 'SQLLog', 'PASS'): [''], + ('!', (('Anonymous', '~ftp'), + ('Limit', 'LOGIN')), + 'DisplayLogin'): [''], + ('', (('Anonymous', '~ftp'), + ('Limit', 'LOGIN')), + 'DisplayFirstChdir'): ['.message'], + ('!', (('Limit', 'WRITE'), ), + 'Allow from', '10.0.0'): ['']}) + + proftpd_object = ProFTPDFormat(document_text, ignore_comments=True) + assert proftpd_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/proftpd_original.conf', 'r') as original_file: + original_text = original_file.read() + proftpd_original_object = ProFTPDFormat(original_text) + + with open('./tests/format/testfiles/proftpd_template.conf', 'r') as template_file: + template_text = template_file.read() + proftpd_template_object = ProFTPDFormat(template_text, + ignore_comments=True) + + proftpd_original_object.join_template(proftpd_template_object) + + with open('./tests/format/testfiles/proftpd_result.conf', 'r') as result_file: + result_text = result_file.read() + + assert proftpd_original_object.get_document_text() == result_text diff --git a/tests/format/test_samba.py b/tests/format/test_samba.py new file mode 100644 index 0000000..8e02b28 --- /dev/null +++ b/tests/format/test_samba.py @@ -0,0 +1,229 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.samba_format import SambaFormat + + +@pytest.mark.samba +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = '''[section name] + parameter name = /home/divanov/Home + other parameter = yes''' + + param_line_1 = OrderedDict({('', 'parameter name'): ['/home/divanov/Home']}) + param_line_2 = OrderedDict({('', 'other parameter'): ['yes']}) + + result = OrderedDict({('', 'section name'): OrderedDict(**param_line_1, + **param_line_2)}) + + samba_object = SambaFormat(document_text) + assert samba_object._document_dictionary == result + + def test_if_input_document_contains_few_parameter_lines_and_some_empty_lines__the_initialized_object_contains_correct_dictionary(self): + document_text = ''' + [section name1] + parameter name = /home/divanov/Home + + other parameter = yes + + + second parameter = 1 + + [section name2] + + other parameter = yes + + ''' + + section_1_content = OrderedDict({('', 'parameter name'): ['/home/divanov/Home'], + ('', 'other parameter'): ['yes'], + ('', 'second parameter'): ['1']}) + + section_2_content = OrderedDict({('', 'other parameter'): ['yes']}) + + result = OrderedDict({('', 'section name1'): section_1_content, + ('', 'section name2'): section_2_content}) + + samba_object = SambaFormat(document_text) + assert samba_object._document_dictionary == result + + def test_if_input_document_contains_sections_with_similar_names_but_different_parameters__the_parameters_merged_in_one_section(self): + document_text = '''[section name] + parameter name = /home/divanov/Home + other parameter = yes + + [section name] + another parameter = 1''' + + section_content = OrderedDict({('', 'parameter name'): ['/home/divanov/Home'], + ('', 'other parameter'): ['yes'], + ('', 'another parameter'): ['1']}) + result = OrderedDict({('', 'section name'): section_content}) + + samba_object = SambaFormat(document_text) + assert samba_object._document_dictionary == result + + def test_if_input_document_contains_sections_with_parameters_with_action_marks__the_key_tuples_of_object_s_dictionary_have_it_as_its_first_element(self): + document_text = '''[section name] + -parameter name = /home/divanov/Home + other parameter = yes + !another parameter = 1''' + + section_content = OrderedDict({('-', 'parameter name'): ['/home/divanov/Home'], + ('', 'other parameter'): ['yes'], + ('!', 'another parameter'): ['1']}) + result = OrderedDict({('', 'section name'): section_content}) + + samba_object = SambaFormat(document_text) + assert samba_object._document_dictionary == result + + def test_if_parameter_in_input_document_has_some_comments__the_comments_will_be_collected_in_the_list_of_parameter_value(self): + document_text = '''# Comment1 + [section name1] + parameter name = /home/divanov/Home + + # Comment2 + # Comment3 + other parameter = yes + + [section name2] + #Comment + !another parameter = 1''' + + section = OrderedDict({'#': ['# Comment1'], + ('', 'parameter name'): ['/home/divanov/Home'], + ('', 'other parameter'): ['# Comment2', + '# Comment3', + 'yes']}) + + result = OrderedDict({('', 'section name1'): section, + ('', 'section name2'): OrderedDict({ + ('!', 'another parameter'): + ['#Comment', '1']})}) + + samba_object = SambaFormat(document_text) + assert samba_object._document_dictionary == result + + def test_if_the_IgnoreComments_flag_is_set__the_parser_ignores_all_comments(self): + document_text = '''# Comment1 + [section name1] + parameter name = /home/divanov/Home + + # Comment2 + # Comment3 + other parameter = yes + + [section name2] + #Comment + !another parameter = 1''' + + section_1 = OrderedDict({('', 'parameter name'): ['/home/divanov/Home'], + ('', 'other parameter'): ['yes']}) + + section_2 = OrderedDict({('!', 'another parameter'): ['1']}) + + result = OrderedDict({('', 'section name1'): section_1, + ('', 'section name2'): section_2}) + + samba_object = SambaFormat(document_text, ignore_comments=True) + assert samba_object._document_dictionary == result + + def test_if_input_document_contains_parameters_to_delete_without_assign_symbol_and_any_values_and_sections_to_delete__the_document_object_contains_dictionary_with_item_to_delete(self): + document_text = ''' + [section name1] + !parameter name = /home/divanov/Home + !other parameter = + !another parameter + [!section name2] + parameter = no + [-section name3] + parameter = no''' + + section_1 = OrderedDict({('!', 'parameter name'): ['/home/divanov/Home'], + ('!', 'other parameter'): [], + ('!', 'another parameter'): []}) + section_2 = OrderedDict({('', 'parameter'): ['no']}) + + result = OrderedDict({('', 'section name1'): section_1, + ('!', 'section name2'): section_2, + ('-', 'section name3'): section_2}) + + samba_object = SambaFormat(document_text) + assert samba_object._document_dictionary == result + + def test_if_joinBefore_flag_is_set__the_document_object_contains_dictionary_with_sections_added_in_the_top_of_it(self): + original_text = '''# Comment1 + [section name2] + parameter name = /home/divanov/Home + + # Comment2 + # Comment3 + other parameter = yes + + [section name3] + #Comment + another parameter = 1''' + + template_text = ''' + [section name1] + parameter name = /homeless/poorness + + one more parameter = oh no + + [section name3] + # Comment + unspoken parameter = 1''' + + result = OrderedDict({('', 'section name1'): + OrderedDict({('', 'parameter name'): + ['/homeless/poorness'], + ('', 'one more parameter'): + ['oh no']}), + ('', 'section name2'): + OrderedDict({'#': ['# Comment1'], + ('', 'parameter name'): + ['/home/divanov/Home'], + ('', 'other parameter'): + ['# Comment2', '# Comment3', 'yes']}), + ('', 'section name3'): + OrderedDict({('', 'another parameter'): + ['#Comment', '1'], + ('', 'unspoken parameter'): + ['1']})}) + + samba_original_object = SambaFormat(original_text, join_before=True) + samba_template_object = SambaFormat(template_text, ignore_comments=True) + samba_original_object.join_template(samba_template_object) + + assert samba_original_object._document_dictionary == result + + def test_if_input_document_parameters_contains_upper_case_symbols__it_becomes_lower_case(self): + document_text = '''[SECTION] + parameter NAME = /home/divanov/Home + Other Parameter = yes''' + + param_line_1 = OrderedDict({('', 'parameter name'): ['/home/divanov/Home']}) + param_line_2 = OrderedDict({('', 'other parameter'): ['yes']}) + + result = OrderedDict({('', 'section'): OrderedDict(**param_line_1, + **param_line_2)}) + + samba_object = SambaFormat(document_text) + assert samba_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/samba_original', 'r') as original_file: + original_text = original_file.read() + samba_original_object = SambaFormat(original_text) + + with open('./tests/format/testfiles/samba_template', 'r') as template_file: + template_text = template_file.read() + samba_template_object = SambaFormat(template_text, + ignore_comments=True) + + samba_original_object.join_template(samba_template_object) + + with open('./tests/format/testfiles/samba_result', 'r') as result_file: + result_text = result_file.read() + + assert samba_original_object.get_document_text() == result_text diff --git a/tests/format/test_xml_gconf.py b/tests/format/test_xml_gconf.py new file mode 100644 index 0000000..402f9f7 --- /dev/null +++ b/tests/format/test_xml_gconf.py @@ -0,0 +1,89 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.xml_gconf_format import XMLGConfFormat + + +@pytest.mark.xml_gconf +class TestParsingMethods: + def test_if_input_document_is_simple_gconf_tree__the_format_object_contains_correct_dictionary(self): + document_text = ''' + + + + + + Something I can never have. + + + True if the command used to handle this type of URL should be run in a terminal. + + + + distributor + + + + + ''' + + result = OrderedDict({('', 'gconf'): + OrderedDict({('', 'dir', ('name', 'gnome')): + OrderedDict({('', 'dir', ('name', 'ymsgr')): + OrderedDict({('', 'entry', ('name', 'needs_terminal')): + OrderedDict({('', 'default', ('type', 'string')): + OrderedDict({('', 'stringvalue'): 'Something I can never have.'}), + ('', 'local_schema', ('short_desc', 'Run the command in a terminal')): + OrderedDict({('', 'longdesc'): 'True if the command used to handle this type of URL should be run in a terminal.'})}), + ('', 'entry', ('name', 'logo_icon_name'), ('mtime', '1342486180'), ('type', 'string')): + OrderedDict({('', 'stringvalue'): 'distributor'})})})})}) + + xml_gconf_object = XMLGConfFormat(document_text) + assert xml_gconf_object._document_dictionary == result + + def test_if_input_document_is_simple_gconf__the_format_object_contains_correct_dictionary(self): + document_text = ''' + + +
  • + grp grp:lwin_toggle +
  • +
    + +
  • + us +
  • +
  • + ru +
  • +
    + + full + +
    + ''' + + result = OrderedDict({('', 'gconf'): + OrderedDict({('', 'entry', ('name', 'options'), ('mtime', '1298136657'), ('type', 'list'), ('ltype', 'string')): ['grp grp:lwin_toggle'], + ('', 'entry', ('name', 'layouts'), ('mtime', '1298136657'), ('type', 'list'), ('ltype', 'string')): + ['us', 'ru'], + ('', 'entry', ('name', 'hinting'), ('mtime', '1298136657'), ('type', 'list')): + OrderedDict({('', 'stringvalue'): 'full'})})}) + + xml_gconf_object = XMLGConfFormat(document_text) + assert xml_gconf_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/xml_gconf_original.xml', 'r') as original_file: + original_text = original_file.read() + xml_gconf_original_object = XMLGConfFormat(original_text) + + with open('./tests/format/testfiles/xml_gconf_template.xml', 'r') as template_file: + template_text = template_file.read() + xml_gconf_template_object = XMLGConfFormat(template_text) + + xml_gconf_original_object.join_template(xml_gconf_template_object) + + with open('./tests/format/testfiles/xml_gconf_result.xml', 'r') as result_file: + result_text = result_file.read() + + assert xml_gconf_original_object.get_document_text() == result_text diff --git a/tests/format/test_xml_xfce.py b/tests/format/test_xml_xfce.py new file mode 100644 index 0000000..49214da --- /dev/null +++ b/tests/format/test_xml_xfce.py @@ -0,0 +1,52 @@ +import pytest +from collections import OrderedDict +from calculate.templates.format.xml_xfce_format import XMLXfceFormat + + +@pytest.mark.xml_xfce +class TestParsingMethods: + def test_if_input_document_contains_just_few_parameter_lines__the_initialised_object_contains_correct_dictionary(self): + document_text = ''' + + + + + + + + + + + + ''' + + net_dict = OrderedDict({('', 'property', 'ThemeName', 'string'): "Calculate", + ('', 'property', 'IconThemeName', 'string'): "Calculate", + ('', 'property', 'DoubleClickTime', 'int'): '400'}) + + xft_dict = OrderedDict({('', 'property', 'Antialias', 'int'): "1", + ('', 'property', 'HintStyle', 'string'): 'hintmedium'}) + + channel_content = OrderedDict({('', 'property', 'Net', 'empty'): net_dict, + ('', 'property', 'Xft', 'empty'): xft_dict}) + + result = OrderedDict({('', 'channel', 'xsettings', '1.0'): channel_content}) + + xml_xfce_object = XMLXfceFormat(document_text) + assert xml_xfce_object._document_dictionary == result + + def test_joining_documents_1(self): + with open('./tests/format/testfiles/xml_xfce_original.xml', 'r') as original_file: + original_text = original_file.read() + xml_xfce_original_object = XMLXfceFormat(original_text) + + with open('./tests/format/testfiles/xml_xfce_template.xml', 'r') as template_file: + template_text = template_file.read() + xml_xfce_template_object = XMLXfceFormat(template_text) + + xml_xfce_original_object.join_template(xml_xfce_template_object) + + with open('./tests/format/testfiles/xml_xfce_result.xml', 'r') as result_file: + result_text = result_file.read() + + assert xml_xfce_original_object.get_document_text() == result_text diff --git a/tests/format/testfiles/a/dir/file1.txt b/tests/format/testfiles/a/dir/file1.txt new file mode 100644 index 0000000..023bd50 --- /dev/null +++ b/tests/format/testfiles/a/dir/file1.txt @@ -0,0 +1,10 @@ +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y +# +# General setup +# +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y diff --git a/tests/format/testfiles/a/dir/file2.txt b/tests/format/testfiles/a/dir/file2.txt new file mode 100644 index 0000000..be7bbc3 --- /dev/null +++ b/tests/format/testfiles/a/dir/file2.txt @@ -0,0 +1,10 @@ +Nine Inch Nails -- Hurt + +I hurt myself today +To see if I still feel +I focused on the pain +The only thing that real +A needle tears a hole +The old familiar sting +Try to kill it all away +But I remember everything. diff --git a/tests/format/testfiles/a1/dir/file1.txt b/tests/format/testfiles/a1/dir/file1.txt new file mode 100644 index 0000000..023bd50 --- /dev/null +++ b/tests/format/testfiles/a1/dir/file1.txt @@ -0,0 +1,10 @@ +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y +# +# General setup +# +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y diff --git a/tests/format/testfiles/b/dir/file1.txt b/tests/format/testfiles/b/dir/file1.txt new file mode 100644 index 0000000..474f6f7 --- /dev/null +++ b/tests/format/testfiles/b/dir/file1.txt @@ -0,0 +1,17 @@ +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y +CONFIG_THREAD_INFO_IN_TASK=y +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=16 +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_BUILD_SALT="" +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y diff --git a/tests/format/testfiles/b/dir/file2.txt b/tests/format/testfiles/b/dir/file2.txt new file mode 100644 index 0000000..acfa977 --- /dev/null +++ b/tests/format/testfiles/b/dir/file2.txt @@ -0,0 +1,16 @@ +Nine Inch Nails -- Hurt + +I hurt myself today +To see if I still feel +I focused on the pain +The only thing that real +A needle tears a hole +The old familiar sting +Try to kill it all away +But I remember everything. + +What have I become +My sweetest friend +Everyone I know +Goes away +In the end diff --git a/tests/format/testfiles/b1/dir/file1.txt b/tests/format/testfiles/b1/dir/file1.txt new file mode 100644 index 0000000..8393bcc --- /dev/null +++ b/tests/format/testfiles/b1/dir/file1.txt @@ -0,0 +1,11 @@ +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y +# +# General setup +# +CONFIG_BUILD_SALT="" +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y diff --git a/tests/format/testfiles/b1/dir/file2.txt b/tests/format/testfiles/b1/dir/file2.txt new file mode 100644 index 0000000..acfa977 --- /dev/null +++ b/tests/format/testfiles/b1/dir/file2.txt @@ -0,0 +1,16 @@ +Nine Inch Nails -- Hurt + +I hurt myself today +To see if I still feel +I focused on the pain +The only thing that real +A needle tears a hole +The old familiar sting +Try to kill it all away +But I remember everything. + +What have I become +My sweetest friend +Everyone I know +Goes away +In the end diff --git a/tests/format/testfiles/bind_original.conf b/tests/format/testfiles/bind_original.conf new file mode 100644 index 0000000..b5d7324 --- /dev/null +++ b/tests/format/testfiles/bind_original.conf @@ -0,0 +1,108 @@ +/* + * Refer to the named.conf(5) and named(8) man pages, and the documentation + * in /usr/share/doc/bind-* for more details. + * Online versions of the documentation can be found here: + * https://kb.isc.org/article/AA-01031 + * + * If you are going to set up an authoritative server, make sure you + * understand the hairy details of how DNS works. Even with simple mistakes, + * you can break connectivity for affected parties, or cause huge amounts of + * useless Internet traffic. + */ + +/* + * You might put in here some ips which are allowed to use the cache or + * recursive queries + */ +acl "trusted" { + 127.0.0.0/8; + 10.0.0.0/8; + 192.168.1.0/24; + ::1/128; +}; + +acl "dns_servers" { + 127.0.0.1; + 10.0.1.3; + 10.1.0.3; + 10.2.0.3; + 10.4.0.3; +}; + +options { + directory "/var/bind"; + pid-file "/run/named/named.pid"; + disable-empty-zone "10.in-addr.arpa"; + + /* https://www.isc.org/solutions/dlv >=bind-9.7.x only */ + //bindkeys-file "/etc/bind/bind.keys"; + + listen-on-v6 { ::1; }; + listen-on { 10.0.0.0/8; 127.0.0.1; }; + + allow-query { + /* + * Accept queries from our "trusted" ACL. We will + * allow anyone to query our master zones below. + * This prevents us from becoming a free DNS server + * to the masses. + */ + trusted; + }; + + allow-query-cache { + /* Use the cache for the "trusted" ACL. */ + trusted; + }; + + allow-recursion { + /* Only trusted addresses are allowed to use recursion. */ + trusted; + }; + + recursion yes; + + response-policy { + zone "rpz.zone"; + }; +}; + +controls { + // local host -- default key. + inet 127.0.0.1 port 953 -allow { 127.0.0.1; "rndc-users"; } + keys { "rndc-remote"; "rndc-key"; }; + inet 127.0.0.1 allow { localhost; }; +}; + +include "/etc/bind/rndc.key"; + +zone "." in { + type hint; + file "/var/bind/named.cache"; +}; + +zone "localhost" IN { + type master; + file "pri/localhost.zone"; + notify no; +}; + +zone "rpz.zone" { + type master; + file "/var/bind/pri/rpz.zone"; + allow-query {trusted;}; + allow-update {none;}; +}; + +# DMZ +zone "dmz.calculate.ru" IN { + type slave; + file "/var/bind/sec/dmz.calculate.ru.zone"; + masters { 10.1.0.3; }; +}; + +zone "1.10.in-addr.arpa" IN { + type slave; + file "/var/bind/sec/dmz.calculate.ru.rev.zone"; + masters { 10.1.0.3; }; +}; diff --git a/tests/format/testfiles/bind_result.conf b/tests/format/testfiles/bind_result.conf new file mode 100644 index 0000000..c2308a4 --- /dev/null +++ b/tests/format/testfiles/bind_result.conf @@ -0,0 +1,131 @@ +/* +* Refer to the named.conf(5) and named(8) man pages, and the documentation +* in /usr/share/doc/bind-* for more details. +* Online versions of the documentation can be found here: +* https://kb.isc.org/article/AA-01031 +* +* If you are going to set up an authoritative server, make sure you +* understand the hairy details of how DNS works. Even with simple mistakes, +* you can break connectivity for affected parties, or cause huge amounts of +* useless Internet traffic. +*/ +/* +* You might put in here some ips which are allowed to use the cache or +* recursive queries +*/ +acl "trusted" { + 127.0.0.0/8; + 10.0.0.0/8; + 192.168.1.0/24; + ::1/128; +}; + +acl "dns_servers" { + 127.0.0.1; + 10.0.1.3; + 10.1.0.3; + 10.2.0.3; + 10.3.0.3; +}; + +options { + directory "/var/bind"; + pid-file "/run/named/named.pid"; + disable-empty-zone "10.in-addr.arpa"; + + /* https://www.isc.org/solutions/dlv >=bind-9.7.x only */ + //bindkeys-file "/etc/bind/bind.keys"; + listen-on-v6 { + ::1; + }; + + listen-on { + 10.0.0.0/8; + 127.0.0.1; + }; + + allow-query { + /* + * Accept queries from our "trusted" ACL. We will + * allow anyone to query our master zones below. + * This prevents us from becoming a free DNS server + * to the masses. + */ + trusted; + }; + recursion yes; + + response-policy { + zone "bad.zone"; + mood "almost.blue"; + }; +}; + +controls { + // local host -- default key. + inet 127.0.0.1 port 953 allow { 127.0.0.1; "rndc-users"; } keys { "rndc-remote"; "rndc-key"; }; + + inet 127.0.0.1 allow { localhost; }; +}; +include "/etc/bind/rndc.key"; + +zone "." in { + type hint; + file "/var/bind/named.cache"; +}; + +zone "localhost" IN { + type master; + file "pri/localhost.zone"; + notify no; +}; + +zone "rpz.zone" { + type master; + file "/var/bind/pri/rpz.zone"; + + allow-query { + untrusted; + }; + + allow-update { + none; + }; +}; + +# DMZ +zone "dmz.calculate.ru" IN { + type slave; + file "/var/bind/sec/spb.calculate.ru.zone"; + + masters { + 10.1.0.3; + }; +}; + +zone "1.10.in-addr.arpa" IN { + type slave; + file "/var/bind/sec/dmz.calculate.ru.rev.zone"; + + masters { + 10.1.0.3; + }; +}; + +zone "msk.calculate.ru" IN { + type slave; + file "/var/bind/sec/msk.calculate.ru.zone"; + + masters { + 10.1.0.3; + }; +}; + +zone "17.2.10.in-addr.arpa" IN { + type slave; + file "/var/bind/sec/phone.spb.calculate.ru.rev.zone"; + + masters { + 10.1.0.3; + }; +}; diff --git a/tests/format/testfiles/bind_template.conf b/tests/format/testfiles/bind_template.conf new file mode 100644 index 0000000..9ec6751 --- /dev/null +++ b/tests/format/testfiles/bind_template.conf @@ -0,0 +1,58 @@ +/* +* Шаблок для проведения теста. +* Этот комментарий не перенесется в оригинальный файл +* после наложения шаблона. +*/ + +// First appearing of this block with replace action mark. +options { + -response-policy { + zone "bad.zone"; + }; +}; + +zone "msk.calculate.ru" IN { + type slave; + file "/var/bind/sec/msk.calculate.ru.zone"; + masters { 10.1.0.3; }; +}; + +zone "17.2.10.in-addr.arpa" IN { + type slave; + file "/var/bind/sec/phone.spb.calculate.ru.rev.zone"; + masters { 10.1.0.3; }; +}; + +# Second appearing of this block in purpose to add one meaningless line in. +options { + response-policy { + mood "almost.blue"; + }; +}; + +/* Replace section */ +zone "dmz.calculate.ru" IN { + type slave; + file "/var/bind/sec/spb.calculate.ru.zone"; + masters { 10.1.0.3; }; +}; + +options { + -allow-recursion {}; +}; + +zone "rpz.zone" { + -allow-query { + untrusted; + }; +}; + +acl "dns_servers" { + 10.3.0.3; + !10.4.0.3; +}; + +options { + !allow-query-cache {}; +}; + diff --git a/tests/format/testfiles/compiz_original b/tests/format/testfiles/compiz_original new file mode 100644 index 0000000..5e2ec68 --- /dev/null +++ b/tests/format/testfiles/compiz_original @@ -0,0 +1,32 @@ +# Compiz test config. +[Added Associations] +application/illustrator=zzz-gimp.desktop +application/pdf=evince.desktop; +application/rtf=libreoffice-writer.desktop; +application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; +application/vnd.oasis.opendocument.text=libreoffice-calc.desktop; +# The Comment have to be saved. +image/bmp=gwenview.desktop; +image/gif=gwenview.desktop; +image/jpeg=gwenview.desktop; +image/jpg=gwenview.desktop; +image/png=gwenview.desktop; +# The Comment to go away. +audio/x-ms-wma=clementine.desktop; +audio/x-wav=clementine.desktop; + +[Non Realistic Section] +audio/mp4=clementine.desktop; +audio/mpeg=clementine.desktop; +audio/x-flac=clementine.desktop; +audio/x-ms-wma=clementine.desktop; +audio/x-wav=clementine.desktop; + +# It is very strange section -- may be It needs to be more strange. +[Strange Section] +video/mp4=smplayer.desktop; +video/mpeg=smplayer.desktop; +video/quicktime=smplayer.desktop; +video/vnd.mpegurl=smplayer.desktop; +video/x-m4v=smplayer.desktop; +video/x-sgi-movie=smplayer.desktop; diff --git a/tests/format/testfiles/compiz_result b/tests/format/testfiles/compiz_result new file mode 100644 index 0000000..9c98873 --- /dev/null +++ b/tests/format/testfiles/compiz_result @@ -0,0 +1,34 @@ +# Compiz test config. +[Added Associations] +application/illustrator=zzz-gimp.desktop +application/pdf=evince.desktop; +application/rtf=libreoffice-writer.desktop; +application/vnd.oasis.opendocument.spreadsheet=calculate-calc.desktop; +application/vnd.oasis.opendocument.text=libreoffice-calc.desktop; +# The Comment have to be saved. +image/bmp=other.desktop; +image/gif=another.desktop; +image/jpeg=gwenview.desktop; +image/jpg=gwenview.desktop; +image/png=gwenview.desktop; +image/x-portable-anymap=gwenview.desktop; +image/x-portable-bitmap=gwenview.desktop; +image/x-portable-graymap=gwenview.desktop; +x-scheme-handler/http=firefox.desktop; +x-scheme-handler/https=firefox.desktop; + +# It is very strange section -- may be It needs to be more strange. +[Strange Section] +video/x-sgi-movie=smplayer.desktop; +text/plain=kwrite.desktop; +x-scheme-handler/mailto=org.kde.kmail2.desktop; +video/ogg=smplayer.desktop; +audio/mp4=clementine.desktop; + +[Audio] +audio/mp4=clementine.desktop; +audio/mpeg=clementine.desktop; +audio/x-flac=clementine.desktop; +audio/x-ms-wma=clementine.desktop; +audio/x-wav=clementine.desktop; + diff --git a/tests/format/testfiles/compiz_template b/tests/format/testfiles/compiz_template new file mode 100644 index 0000000..e76310b --- /dev/null +++ b/tests/format/testfiles/compiz_template @@ -0,0 +1,39 @@ + +[Added Associations] +image/x-portable-anymap=gwenview.desktop; +image/x-portable-bitmap=gwenview.desktop; +image/x-portable-graymap=gwenview.desktop; + +[Added Associations] +image/bmp=other.desktop; +image/gif=another.desktop; + +# Let's remove non realistic section. +[!Non Realistic Section] + +[Added Associations] +x-scheme-handler/http=firefox.desktop; +x-scheme-handler/https=firefox.desktop; + +# New section for audio +[Audio] +audio/mp4=clementine.desktop; +audio/mpeg=clementine.desktop; +audio/x-flac=clementine.desktop; +audio/x-ms-wma=clementine.desktop; +audio/x-wav=clementine.desktop; + +[Added Associations] +!audio/mp4 +!audio/mpeg +!audio/x-flac= +!audio/x-ms-wma= +!audio/x-wav + +# To make strange section more strange. +[-Strange Section] +video/x-sgi-movie=smplayer.desktop; +text/plain=kwrite.desktop; +x-scheme-handler/mailto=org.kde.kmail2.desktop; +video/ogg=smplayer.desktop; +audio/mp4=clementine.desktop; diff --git a/tests/format/testfiles/diff_1.patch b/tests/format/testfiles/diff_1.patch new file mode 100644 index 0000000..14edf64 --- /dev/null +++ b/tests/format/testfiles/diff_1.patch @@ -0,0 +1,34 @@ +diff -urN a/dir/file1.txt b/dir/file1.txt +--- a/dir/file1.txt 2020-01-29 14:38:21.216540100 +0300 ++++ b/dir/file1.txt 2020-01-29 14:40:19.200542500 +0300 +@@ -1,10 +1,17 @@ + CONFIG_IRQ_WORK=y + CONFIG_BUILDTIME_EXTABLE_SORT=y ++CONFIG_THREAD_INFO_IN_TASK=y + # + # General setup + # ++CONFIG_INIT_ENV_ARG_LIMIT=16 ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_BUILD_SALT="" + CONFIG_HAVE_KERNEL_GZIP=y + CONFIG_HAVE_KERNEL_BZIP2=y ++CONFIG_HAVE_KERNEL_LZMA=y + CONFIG_HAVE_KERNEL_XZ=y + CONFIG_HAVE_KERNEL_LZO=y + CONFIG_HAVE_KERNEL_LZ4=y +diff -urN a/dir/file2.txt b/dir/file2.txt +--- a/dir/file2.txt 2020-01-29 14:35:45.112536800 +0300 ++++ b/dir/file2.txt 2020-01-29 13:47:05.475437800 +0300 +@@ -8,3 +8,9 @@ + The old familiar sting + Try to kill it all away + But I remember everything. ++ ++What have I become ++My sweetest friend ++Everyone I know ++Goes away ++In the end diff --git a/tests/format/testfiles/diff_2.patch b/tests/format/testfiles/diff_2.patch new file mode 100644 index 0000000..eecd2c6 --- /dev/null +++ b/tests/format/testfiles/diff_2.patch @@ -0,0 +1,41 @@ +diff -Naru a1/dir/file1.txt b1/dir/file1.txt +--- a1/dir/file1.txt 2020-01-29 16:12:32.179927200 +0300 ++++ b1/dir/file1.txt 2020-01-29 15:54:03.080863300 +0300 +@@ -1,10 +1,17 @@ + CONFIG_IRQ_WORK=y + CONFIG_BUILDTIME_EXTABLE_SORT=y ++CONFIG_THREAD_INFO_IN_TASK=y + # + # General setup + # ++CONFIG_INIT_ENV_ARG_LIMIT=16 ++# CONFIG_COMPILE_TEST is not set ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_BUILD_SALT="" + CONFIG_HAVE_KERNEL_GZIP=y + CONFIG_HAVE_KERNEL_BZIP2=y ++CONFIG_HAVE_KERNEL_LZMA=y + CONFIG_HAVE_KERNEL_XZ=y + CONFIG_HAVE_KERNEL_LZO=y + CONFIG_HAVE_KERNEL_LZ4=y +diff -Naru a1/dir/file2.txt b1/dir/file2.txt +--- a1/dir/file2.txt 1970-01-01 03:00:00.000000000 +0300 ++++ b1/dir/file2.txt 2020-01-29 13:47:05.475437800 +0300 +@@ -0,0 +1,16 @@ ++Nine Inch Nails -- Hurt ++ ++I hurt myself today ++To see if I still feel ++I focused on the pain ++The only thing that real ++A needle tears a hole ++The old familiar sting ++Try to kill it all away ++But I remember everything. ++ ++What have I become ++My sweetest friend ++Everyone I know ++Goes away ++In the end diff --git a/tests/format/testfiles/dovecot_original.conf b/tests/format/testfiles/dovecot_original.conf new file mode 100644 index 0000000..eda5bb8 --- /dev/null +++ b/tests/format/testfiles/dovecot_original.conf @@ -0,0 +1,38 @@ +# 2.2.32 (dfbe293d4): /usr/local/etc/dovecot/dovecot.conf +# Pigeonhole version 0.4.20 (7cd71ba) +# OS: FreeBSD 10.3-RELEASE-p20 amd64 + +auth_default_realm = domain.com +auth_mechanisms = plain login +auth_realms = domain.com domain2.com + +# Log unsuccessful authentication attempts and the reasons why they failed. +auth_verbose = no + +# In case of password mismatches, log the attempted password. Valid values are +# no, plain and sha1. sha1 can be useful for detecting brute force password +# attempts vs. user simply trying the same password over and over again. +auth_verbose_passwords = no + +dict { +acl = mysql:/usr/local/etc/dovecot/shared-folders.conf +sqlquota = mysql:/usr/local/etc/dovecot/quota.conf +} + +hostname = mail.domain.com + +namespace inbox { + # These mailboxes are widely used and could perhaps be created automatically: + mailbox Drafts { + special_use = \Drafts + } + mailbox Junk { + special_use = \Junk + } + mailbox Trash { + special_use = \Trash + } +} + +!include auth-passwdfile.conf.ext +!include auth-checkpassword.conf.ext diff --git a/tests/format/testfiles/dovecot_result.conf b/tests/format/testfiles/dovecot_result.conf new file mode 100644 index 0000000..f367e88 --- /dev/null +++ b/tests/format/testfiles/dovecot_result.conf @@ -0,0 +1,47 @@ +# 2.2.32 (dfbe293d4): /usr/local/etc/dovecot/dovecot.conf +# Pigeonhole version 0.4.20 (7cd71ba) +# OS: FreeBSD 10.3-RELEASE-p20 amd64 +auth_default_realm = domain.com + +auth_realms = domain.com domain2.com + +# Log unsuccessful authentication attempts and the reasons why they failed. +auth_verbose = yes + +# In case of password mismatches, log the attempted password. Valid values are +# no, plain and sha1. sha1 can be useful for detecting brute force password +# attempts vs. user simply trying the same password over and over again. +auth_verbose_passwords = no + +dict { + acl = mysql:/usr/local/etc/dovecot/shared-folders.conf + + sqlquota = mysql:/etc/dovecot/quota.conf + + something_strange = val:12, /something/meaningless.conf +} + +hostname = mail.domain.com + +namespace inbox { + # These mailboxes are widely used and could perhaps be created automatically: + mailbox Drafts { + special_use = \Something else + } + + mailbox Trash { + special_use = \Disgusting Trash + + useless_parameter = 13 + } + + mailbox Archives { + auto = subscribe + + special_use = \Archive + } +} + +!include auth-passwdfile.conf.ext + +!include auth-importantextension.conf.ext diff --git a/tests/format/testfiles/dovecot_template.conf b/tests/format/testfiles/dovecot_template.conf new file mode 100644 index 0000000..0902562 --- /dev/null +++ b/tests/format/testfiles/dovecot_template.conf @@ -0,0 +1,39 @@ +namespace inbox { + mailbox Drafts { + special_use = \Something else + } +} + +!auth_mechanisms + +namespace inbox { + mailbox Archives { + auto = subscribe + special_use = \Archive + } +} + +!!include auth-checkpassword.conf.ext + +!include auth-importantextension.conf.ext + +namespace inbox { + !mailbox Junk { + + } + + -mailbox Trash { + special_use = \Disgusting Trash + useless_parameter = 13 + } +} + +# The comment that have to be ignored. +auth_verbose = yes + +dict { + sqlquota = mysql:/etc/dovecot/quota.conf + + something_strange = val:12, /something/meaningless.conf +} + diff --git a/tests/format/testfiles/json_original.json b/tests/format/testfiles/json_original.json new file mode 100644 index 0000000..4bce939 --- /dev/null +++ b/tests/format/testfiles/json_original.json @@ -0,0 +1,21 @@ +{ + "Param1":"ParamValue1", + "Param2":"ParamValue2", + "ParamList":["Param1", "Param2", "Param3"], + "Param3": 1, + "Param4": 0, + "BlockParam1":{ + "BlockParam1":1, + "BlockParam2":0 + }, + "BlockParam2":{ + "BlockParam1":1, + "BlockParam2":"Special value" + }, + "BlockParam3":{ + "BlockParam1": false, + "BlockParam2": 0, + "BlockParam3": "Special value" + }, + "Param5": true +} diff --git a/tests/format/testfiles/json_result.json b/tests/format/testfiles/json_result.json new file mode 100644 index 0000000..511d0c7 --- /dev/null +++ b/tests/format/testfiles/json_result.json @@ -0,0 +1,25 @@ +{ + "Param1": "NewParamValue1", + "Param2": "ParamValue2", + "ParamList": [ + "Param1", + "Param2", + "Param3" + ], + "Param3": 1, + "Param4": 0, + "BlockParam1": { + "BlockParam3": true, + "BlockParam4": [ + "Param4", + "Param5", + "Param6" + ] + }, + "BlockParam2": { + "BlockParam1": 0, + "BlockParam2": "Special value", + "BlockParam3": true + }, + "Param6": 37 +} \ No newline at end of file diff --git a/tests/format/testfiles/json_template.json b/tests/format/testfiles/json_template.json new file mode 100644 index 0000000..2d771d4 --- /dev/null +++ b/tests/format/testfiles/json_template.json @@ -0,0 +1,14 @@ +{ + "Param1":"NewParamValue1", + "-BlockParam1":{ + "BlockParam3":true, + "BlockParam4":["Param4", "Param5", "Param6"] + }, + "BlockParam2":{ + "BlockParam1" : 0, + "BlockParam3" : true + }, + "!BlockParam3":{}, + "!Param5" : false, + "Param6" : 37 +} diff --git a/tests/format/testfiles/kde_original b/tests/format/testfiles/kde_original new file mode 100644 index 0000000..dac4e6b --- /dev/null +++ b/tests/format/testfiles/kde_original @@ -0,0 +1,63 @@ +# KDE or Plasma config file. +# Part from Plasma +[PlasmaViews][Panel 69][Horizontal1024] +alignment=132 +length=674 +maxLength=674 +minLength=674 +panelVisibility=1 +thickness=56 + +[PlasmaViews][Panel 69][Horizontal1152] +# Comment have to be saved. +alignment=132 +length=674 +maxLength=674 +minLength=674 +panelVisibility=1 +thickness=56 + +[FileDialogSize] +Height 1080=466 +Width 1920=747 + +[KFileDialog Settings] +Recent Files[$e]=start-here-blue.svg,/usr/share/pixmaps/calculate/start-here-blue.svg +Recent URLs[$e]=/usr/share/pixmaps/calculate/ + +# Very important comment -- need to be saved. +[KPropertiesDialog] +Height 1080=453 +Width 1920=512 + +[PlasmaRunnerManager] +LaunchCounts=None +pluginWhiteList=services,shell,bookmarks,baloosearch,locations + +[PlasmaViews][Panel 69] +alignment=132 +panelVisibility=1 + +# Part from KDE +[Desktop Entry] +Type=Application +Exec=konversation -qwindowtitle %c %u +Icon=/usr/share/icons/hicolor/64x64/apps/konversation.png +X-DocPath=konversation/index.html +GenericName=IRC Client +GenericName[ar]=عميل IRC +GenericName[ast]=Veceru IRC +GenericName[be]=Кліент IRC +GenericName[bg]=IRC клиент +GenericName[bs]=IRC klijent +GenericName[ca]=Client d'IRC +GenericName[ca@valencia]=Client d'IRC +GenericName[cs]=IRC klient +GenericName[da]=IRC-klient +GenericName[de]=IRC-Programm +Categories=Qt;KDE;Network;IRCClient; +X-KDE-ServiceTypes=DBUS/InstantMessenger +X-DBUS-StartupType=Unique +X-DBUS-ServiceName=org.kde.konversation +StartupNotify=true +NoDisplay=true diff --git a/tests/format/testfiles/kde_result b/tests/format/testfiles/kde_result new file mode 100644 index 0000000..39aded7 --- /dev/null +++ b/tests/format/testfiles/kde_result @@ -0,0 +1,75 @@ +# KDE or Plasma config file. +# Part from Plasma +[PlasmaViews][Panel 69][Horizontal1024] +alignment=132 +length=674 +maxLength=674 +minLength=674 +panelVisibility=1 +thickness=56 + +[PlasmaViews][Panel 69][Horizontal1152] +# Comment have to be saved. +alignment=666 +maxLength=674 +minLength=674 +panelVisibility=0 +thickness=56 + +[FileDialogSize] +Height 1080=466 +Width 1920=747 + +[KFileDialog Settings] +Recent Files[$e]=start-here-blue.svg,/usr/share/pixmaps/calculate/start-here-blue.svg +Recent URLs[$e]=/usr/share/pixmaps/calculate/ + +# Very important comment -- need to be saved. +[KPropertiesDialog] +Width 1920=486 +Height 1080=123 + +[PlasmaRunnerManager] +LaunchCounts=None +pluginWhiteList=services,shell,bookmarks,baloosearch,locations + +# Part from KDE +[Desktop Entry] +Type=Application +Exec=konversation -qwindowtitle %c %u +Icon=/usr/share/icons/hicolor/64x64/apps/konversation.png +X-DocPath=konversation/index.html +GenericName=IRC Client +GenericName[ast]=Veceru IRC +GenericName[be]=Кліент IRC +GenericName[bg]=IRC клиент +GenericName[bs]=IRC klijent +GenericName[ca]=Client d'IRC +GenericName[ca@valencia]=Client d'IRC +GenericName[cs]=IRC klient +GenericName[da]=IRC-klient +GenericName[de]=IRC-Programm +Categories=Qt;KDE;Network;IRCClient; +X-KDE-ServiceTypes=DBUS/InstantMessenger +X-DBUS-StartupType=Unique +X-DBUS-ServiceName=org.kde.konversation +StartupNotify=true +NoDisplay=true +GenericName[el]=Πελάτης IRC +GenericName[en_GB]=IRC Client +GenericName[es]=Cliente de IRC +GenericName[et]=IRC klient +GenericName[eu]=IRC bezeroa +GenericName[fi]=IRC-keskustelu +GenericName[fr]=Client IRC +GenericName[ga]=Cliant IRC +GenericName[gl]=Cliente de IRC +GenericName[he]=לקוח IRC + +[PlasmaViews][Panel 69][Horizontal1200] +alignment=132 +length=674 +maxLength=674 +minLength=674 +thickness=56 + diff --git a/tests/format/testfiles/kde_template b/tests/format/testfiles/kde_template new file mode 100644 index 0000000..549443b --- /dev/null +++ b/tests/format/testfiles/kde_template @@ -0,0 +1,40 @@ + +# New comment for test. +[PlasmaViews][Panel 69][Horizontal1152] +alignment=666 +!length=674 +panelVisibility=0 +# thickness= + +[Desktop Entry] +GenericName[de]=IRC-Programm +GenericName[el]=Πελάτης IRC +GenericName[en_GB]=IRC Client +GenericName[es]=Cliente de IRC +GenericName[et]=IRC klient + +# New section +[PlasmaViews][Panel 69][Horizontal1200] +alignment=132 +length=674 +maxLength=674 +minLength=674 +!panelVisibility=1 +thickness=56 + +[!PlasmaViews][Panel 69] + + +[-KPropertiesDialog] +Width 1920=486 +Height 1080=123 +# Thickness + +[Desktop Entry] +GenericName[eu]=IRC bezeroa +GenericName[fi]=IRC-keskustelu +GenericName[fr]=Client IRC +GenericName[ga]=Cliant IRC +GenericName[gl]=Cliente de IRC +GenericName[he]=לקוח IRC +!GenericName[ar] diff --git a/tests/format/testfiles/kernel_original b/tests/format/testfiles/kernel_original new file mode 100644 index 0000000..c5ef5c5 --- /dev/null +++ b/tests/format/testfiles/kernel_original @@ -0,0 +1,39 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/x86 4.19.85-calculate Kernel Configuration +# + +# +# Compiler: gcc (Gentoo 9.2.0-r2 p3) 9.2.0 +# +CONFIG_CC_IS_GCC=y +CONFIG_GCC_VERSION=90200 +CONFIG_CLANG_VERSION=0 +CONFIG_CC_HAS_ASM_GOTO=y +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y +CONFIG_THREAD_INFO_IN_TASK=y + +#Comment, that can not exist. +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=n +CONFIG_EXT3_FS_XATTR=m + +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=32 +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_BUILD_SALT="" +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y diff --git a/tests/format/testfiles/kernel_result b/tests/format/testfiles/kernel_result new file mode 100644 index 0000000..0c92229 --- /dev/null +++ b/tests/format/testfiles/kernel_result @@ -0,0 +1,34 @@ +# +# Automatically generated file; DO NOT EDIT. +# Linux/x86 4.19.85-calculate Kernel Configuration +# +# +# Compiler: gcc (Gentoo 9.2.0-r2 p3) 9.2.0 +# +CONFIG_CC_IS_GCC=y +CONFIG_GCC_VERSION=90200 +CONFIG_CLANG_VERSION=0 +CONFIG_CC_HAS_ASM_GOTO=y +CONFIG_IRQ_WORK=y +CONFIG_BUILDTIME_EXTABLE_SORT=y +CONFIG_THREAD_INFO_IN_TASK=y +# +# General setup +# +CONFIG_INIT_ENV_ARG_LIMIT=16 +# CONFIG_COMPILE_TEST is not set +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_BUILD_SALT="" +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_XZ=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_KERNEL_LZ4=y +# CONFIG_KERNEL_GZIP is not set +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +CONFIG_KERNEL_XZ=y +CONFIG_XFS_FS=m +CONFIG_REISERFS_FS=y diff --git a/tests/format/testfiles/kernel_template b/tests/format/testfiles/kernel_template new file mode 100644 index 0000000..dcff829 --- /dev/null +++ b/tests/format/testfiles/kernel_template @@ -0,0 +1,10 @@ + +# Calculate format=kernel -- this comment will be not saved. +CONFIG_XFS_FS=m +CONFIG_REISERFS_FS=y +# CONFIG_EXT3_FS is not set +!CONFIG_EXT3_FS_POSIX_ACL= +!CONFIG_EXT3_FS_SECURITY= +!CONFIG_EXT3_FS_XATTR + +CONFIG_INIT_ENV_ARG_LIMIT=16 diff --git a/tests/format/testfiles/ldap_logic_lines_test.txt b/tests/format/testfiles/ldap_logic_lines_test.txt new file mode 100644 index 0000000..ca1c1ab --- /dev/null +++ b/tests/format/testfiles/ldap_logic_lines_test.txt @@ -0,0 +1,9 @@ +First string + of test file. +Second + string of + test file. +Third string of test file. + +Fourth string + of test file. diff --git a/tests/format/testfiles/ldap_original.conf b/tests/format/testfiles/ldap_original.conf new file mode 100644 index 0000000..ce9da5b --- /dev/null +++ b/tests/format/testfiles/ldap_original.conf @@ -0,0 +1,71 @@ +#------------------------------------------------------------------------------ +# Modified Calculate-server 2.1.16 +# File of a profile "profile/samba" +#------------------------------------------------------------------------------ + +include /etc/openldap/schema/core.schema +include /etc/openldap/schema/cosine.schema +include /etc/openldap/schema/nis.schema +include /etc/openldap/schema/inetorgperson.schema +pidfile /var/run/openldap/slapd.pid +argsfile /var/run/openldap/slapd.arg +loglevel 0 +allow bind_v2 +modulepath /usr/lib/openldap/openldap +access to attrs=userPassword + by dn="cn=ldapadmin,dc=calculate" write + by dn="ou=Samba,ou=Services,dc=calculate" write + by dn="ou=Unix,ou=Services,dc=calculate" write + by dn="ou=Mail,ou=Services,dc=calculate" read + by dn="ou=Jabber,ou=Services,dc=calculate" read + by dn="ou=Ftp,ou=Services,dc=calculate" read + by dn="ou=Replication,ou=LDAP,ou=Services,dc=calculate" write + by self read + by * auth +access to attrs=sambaLMPassword,sambaNTPassword + by dn="cn=ldapadmin,dc=calculate" write + by dn="ou=Samba,ou=Services,dc=calculate" write + by dn="ou=Replication,ou=LDAP,ou=Services,dc=calculate" write + by * none +access to dn.base="cn=proxyuser,dc=calculate" + by dn="cn=ldapadmin,dc=calculate" write + by dn="cn=proxyuser,dc=calculate" read + by * none +access to * + by dn="cn=ldapadmin,dc=calculate" write + by self write + by * read + +# Backend section for test. +backend bdb +suffix "dc=calculate" + +database bdb +suffix "dc=example, dc=calculate" +checkpoint 1024 5 +cachesize 10000 +sizelimit unlimited +directory /var/lib/openldap-data +rootdn "cn=ldaproot,dc=calculate" +#include /etc/openldap/replication.conf +index objectClass eq +index cn pres,sub,eq +index sn pres,sub,eq +index uid pres,sub,eq +index uidNumber eq +index gidNumber eq +index default sub +syncrepl rid=123 + provider=ldap://provider.example.com:389 + type=refreshOnly + interval=01:00:00:00 + searchbase="dc=example, dc=com" + filter="(objectClass=organizationalPerson)" + scope=sub + attrs="cn, sn, ou, telephoneNumber, title, l" + schemachecking=off + bindmethod=simple + binddn="cn=syncuser, dc=example, dc=com" + credentials=secret + +updateref ldap://ldapmaster.example.com diff --git a/tests/format/testfiles/ldap_result.conf b/tests/format/testfiles/ldap_result.conf new file mode 100644 index 0000000..c0ea9e6 --- /dev/null +++ b/tests/format/testfiles/ldap_result.conf @@ -0,0 +1,67 @@ +#------------------------------------------------------------------------------ +# Modified Calculate-server 2.1.16 +# File of a profile "profile/samba" +#------------------------------------------------------------------------------ +include /etc/openldap/schema/cosine.schema +include /etc/openldap/schema/nis.schema +include /etc/openldap/schema/inetorgperson.schema +include /etc/openldap/schema/otherschema.schema +pidfile /var/run/openldap/slapd.pid +argsfile /var/run/openldap/slapd.arg +loglevel 0 +allow bind_v2 +modulepath /usr/lib/openldap/openldap +access to attrs=userPassword + by dn="cn=newother, dc=calculate" read + by dn="cn=newval, dc=calculate" read + by dn="cn=ldapadmin,dc=calculate" write + by dn="ou=Samba,ou=Services,dc=calculate" write + by dn="ou=Unix,ou=Services,dc=calculate" write + by dn="ou=Mail,ou=Services,dc=calculate" read + by dn="ou=Jabber,ou=Services,dc=calculate" read + by dn="ou=Replication,ou=LDAP,ou=Services,dc=calculate" read + by self read + by * none +access to attrs=sambaLMPassword,sambaNTPassword + by dn="ou=Jabber,ou=Services,dc=calculate" read + by dn="ou=Mail,ou=Services,dc=calculate" read + by dn="cn=newval, dc=calculate" read + by dn="ou=Unix,ou=Services,dc=calculate" write + by dn="ou=Replication,ou=LDAP,ou=Services,dc=calculate" write + by dn="ou=Samba,ou=Services,dc=calculate" write + by dn="cn=ldapadmin,dc=calculate" read + by self read + by * auth +access to * by * read + +# Backend section for test. +backend bdb +suffix "dc=example" +rootdn "cn=ldaproot,dc=calculate" +modulepath /usr/lib/openldap + +database bdb +include /etc/openldap/replication.conf +suffix "dc=example, dc=calculate" +checkpoint 1024 5 +cachesize 10000 +sizelimit unlimited +directory /var/lib/openldap-data +rootdn "cn=ldaproot,dc=calculate" +#include /etc/openldap/replication.conf +index objectClass eq +index cn pres,sub,eq +index uid pres,sub,eq +index uidNumber eq +index gidNumber eq +index default sub +syncrepl rid=001 + provider=ldap://provider.newexample.com:389 + type=refreshOnly + interval=01:00:00:00 + searchbase="dc=example,dc=com" +updateref ldap://ldapmaster.newexample.com +modulepath /usr/lib/openldap +moduleload accesslog.la +moduleload syncprov.la + diff --git a/tests/format/testfiles/ldap_template.conf b/tests/format/testfiles/ldap_template.conf new file mode 100644 index 0000000..df6e199 --- /dev/null +++ b/tests/format/testfiles/ldap_template.conf @@ -0,0 +1,71 @@ +# After deleting of this directive, comment from top of original file will be + saved. +!include /etc/openldap/schema/core.schema +include /etc/openldap/schema/otherschema.schema + +access to attrs=userPassword + by dn="cn=newval, dc=calculate" read + by dn="cn=ldapadmin,dc=calculate" write + by dn="ou=Samba,ou=Services,dc=calculate" write + by !dn="ou=Ftp,ou=Services,dc=calculate" read + by dn="ou=Replication,ou=LDAP,ou=Services,dc=calculate" read + by * none + +-access to attrs=sambaLMPassword,sambaNTPassword + by dn="ou=Replication,ou=LDAP,ou=Services,dc=calculate" write + by dn="ou=Samba,ou=Services,dc=calculate" write + by dn="cn=ldapadmin,dc=calculate" read + by self read + by * auth + +!access to dn.base="cn=proxyuser,dc=calculate" + +access to * + by !self + by !dn="cn=ldapadmin,dc=calculate" write + +access to attrs=userPassword + by dn="cn=newother, dc=calculate" read + +# Unix +access to attrs=sambaLMPassword,sambaNTPassword + by dn="ou=Unix,ou=Services,dc=calculate" write + +# Newval +access to attrs=sambaLMPassword,sambaNTPassword + by dn="cn=newval, dc=calculate" read + +# Mail +access to attrs=sambaLMPassword,sambaNTPassword + by dn="ou=Mail,ou=Services,dc=calculate" read + +# Jabber +access to attrs=sambaLMPassword,sambaNTPassword + by dn="ou=Jabber,ou=Services,dc=calculate" read + +# Comment to backend section from template. +backend bdb +rootdn "cn=ldaproot,dc=calculate" +suffix "dc=example" + +database bdb +!syncrepl rid=123 +include /etc/openldap/replication.conf +!index sn + +!updateref ldap://ldapmaster.example.com + +syncrepl rid=001 + provider=ldap://provider.newexample.com:389 + type=refreshOnly + interval=01:00:00:00 + searchbase="dc=example,dc=com" + +updateref ldap://ldapmaster.newexample.com + +modulepath /usr/lib/openldap +moduleload accesslog.la +moduleload syncprov.la + +backend bdb +modulepath /usr/lib/openldap diff --git a/tests/format/testfiles/logic_lines_test.txt b/tests/format/testfiles/logic_lines_test.txt new file mode 100644 index 0000000..009dd82 --- /dev/null +++ b/tests/format/testfiles/logic_lines_test.txt @@ -0,0 +1,5 @@ +First string of test file. +Second string of test file. +Third string of test file. + +Fourth string of test file. diff --git a/tests/format/testfiles/logic_lines_test_input.txt b/tests/format/testfiles/logic_lines_test_input.txt new file mode 100644 index 0000000..b0a841c --- /dev/null +++ b/tests/format/testfiles/logic_lines_test_input.txt @@ -0,0 +1,41 @@ +#Определяет учетную запись, которая является владельцем почтовой очереди. +myhostname = mail.spb.calculate.ru + +#Определяет домен данного компьютера. По умолчанию преобразуется из myhostname +#mydomain = local.calculate.ru + +#Это имя добавляется локально посланным сообщениям. +#Например: если вы посылаете сообщение от пользователя root, +#то он его получит таким: from root@mta1.domain.com. +#myorigin = $mydomain + +#Эта директива указывает postfix что он должен принять почту для +#пользователя этого домена. Пожалуйста, учтите, что данная директива НЕ +#может быть использована для витруального домена или для запасного MX +#хоста. В нашем случае, мы сделаем так, чтобы принималась почта для +#пользователей следующих доменов: +mydestination = $myhostname, localhost.$mydomain, $mydomain + +#Разрешенные адреса получателей указаны в параметре virtual_alias_maps. SMTP +#сервер Postfix отклоняет недопустимые адреса получателей с сообщением "User +#unknown in virtual alias table (Пользователь неизвестен в таблице #виртуальных алиасов)". +virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf, \ +ldap:/etc/postfix/ldap-aliases-gr.cf, ldap:/etc/postfix/ldap-aliases-repl.cf + + +# The unknown_local_recipient_reject_code specifies the SMTP server +# response code when a recipient domain matches $mydestination or +# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty +# and the recipient address or address local-part is not found. +unknown_local_recipient_reject_code = 550 + +#Эта директива указывает postfix какие сети он должен считать +#локальными. Другими словами, компьютеры, соединяющиеся из этих сетей +#будут иметь возможность релея почтовых сообщений. В нашем случае мы +#прописываем следующие сети 127.0.0.0 (для localhost, собственно сам +#сервер), 192.168.10.0 (для всех других компьютеров в нашей внешней сети), +#и 192.168.0.0 (для всех компьютеров во внутренней сети). +# 93.100.239.44 - Алексей Пестриков +# 109.167.151.108 - lautre.ru +mynetworks = 10.0.0.0/8, 80.246.243.18, 95.213.228.194, 94.159.1.246, \ +109.167.151.108, 80.246.245.82, 80.246.248.234, 93.100.239.44 diff --git a/tests/format/testfiles/logic_lines_test_output.txt b/tests/format/testfiles/logic_lines_test_output.txt new file mode 100644 index 0000000..35211f6 --- /dev/null +++ b/tests/format/testfiles/logic_lines_test_output.txt @@ -0,0 +1,39 @@ +#Определяет учетную запись, которая является владельцем почтовой очереди. +myhostname = mail.spb.calculate.ru + +#Определяет домен данного компьютера. По умолчанию преобразуется из myhostname +#mydomain = local.calculate.ru + +#Это имя добавляется локально посланным сообщениям. +#Например: если вы посылаете сообщение от пользователя root, +#то он его получит таким: from root@mta1.domain.com. +#myorigin = $mydomain + +#Эта директива указывает postfix что он должен принять почту для +#пользователя этого домена. Пожалуйста, учтите, что данная директива НЕ +#может быть использована для витруального домена или для запасного MX +#хоста. В нашем случае, мы сделаем так, чтобы принималась почта для +#пользователей следующих доменов: +mydestination = $myhostname, localhost.$mydomain, $mydomain + +#Разрешенные адреса получателей указаны в параметре virtual_alias_maps. SMTP +#сервер Postfix отклоняет недопустимые адреса получателей с сообщением "User +#unknown in virtual alias table (Пользователь неизвестен в таблице #виртуальных алиасов)". +virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf, ldap:/etc/postfix/ldap-aliases-gr.cf, ldap:/etc/postfix/ldap-aliases-repl.cf + + +# The unknown_local_recipient_reject_code specifies the SMTP server +# response code when a recipient domain matches $mydestination or +# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty +# and the recipient address or address local-part is not found. +unknown_local_recipient_reject_code = 550 + +#Эта директива указывает postfix какие сети он должен считать +#локальными. Другими словами, компьютеры, соединяющиеся из этих сетей +#будут иметь возможность релея почтовых сообщений. В нашем случае мы +#прописываем следующие сети 127.0.0.0 (для localhost, собственно сам +#сервер), 192.168.10.0 (для всех других компьютеров в нашей внешней сети), +#и 192.168.0.0 (для всех компьютеров во внутренней сети). +# 93.100.239.44 - Алексей Пестриков +# 109.167.151.108 - lautre.ru +mynetworks = 10.0.0.0/8, 80.246.243.18, 95.213.228.194, 94.159.1.246, 109.167.151.108, 80.246.245.82, 80.246.248.234, 93.100.239.44 diff --git a/tests/format/testfiles/openrc_original b/tests/format/testfiles/openrc_original new file mode 100644 index 0000000..658b794 --- /dev/null +++ b/tests/format/testfiles/openrc_original @@ -0,0 +1,37 @@ +# It is used in the consolefont, keymaps, numlock and termencoding +# service scripts. +rc_tty_number=12 + +# The following setting turns on the memory.use_hierarchy setting in the +# root memory cgroup for cgroups v1. +# It must be set to yes in this file if you want this functionality. +rc_cgroup_memory_use_hierarchy="NO" + +# This sets the mode used to mount cgroups. +# "hybrid" mounts cgroups version 2 on /sys/fs/cgroup/unified and +# cgroups version 1 on /sys/fs/cgroup. +# "legacy" mounts cgroups version 1 on /sys/fs/cgroup +# "unified" mounts cgroups version 2 on /sys/fs/cgroup +rc_cgroup_mode="hybrid" + +# Multiple settings and values can be specified. +# For example, you would use this to set the maximum memory and maximum +# number of pids for a service. +rc_cgroup_settings="10485760" + +# If you have multiple slapd instances per #376699, this will provide a default config +INSTANCE="openldap${SVCNAME#slapd}" + +# If you use the classical configuration file: +OPTS_CONF="-f /etc/${INSTANCE}/slapd.conf" + +# Specify the kerberos keytab file +#KRB5_KTNAME=/etc/openldap/krb5-ldap.keytab +c_need="!net net.lo" + +# setting rc_send_sigkill to no. +rc_cgroup_cleanup="NO" + +# If this is yes, we will send sighup to the processes in the cgroup +# immediately after stopsig and sigcont. +rc_send_sighup="YES" diff --git a/tests/format/testfiles/openrc_result b/tests/format/testfiles/openrc_result new file mode 100644 index 0000000..4540280 --- /dev/null +++ b/tests/format/testfiles/openrc_result @@ -0,0 +1,37 @@ +# It is used in the consolefont, keymaps, numlock and termencoding +# service scripts. +rc_tty_number=12 + +# The following setting turns on the memory.use_hierarchy setting in the +# root memory cgroup for cgroups v1. +# It must be set to yes in this file if you want this functionality. +rc_cgroup_memory_use_hierarchy="YES" + +# Multiple settings and values can be specified. +# For example, you would use this to set the maximum memory and maximum +# number of pids for a service. +rc_cgroup_settings="10485760" + +# If you have multiple slapd instances per #376699, this will provide a default config +instance="openldap${SVCNAME#slapd}" + +# If you use the classical configuration file: +opts_conf="-f /etc/${INSTANCE}/slapd.conf" + +# Specify the kerberos keytab file +#KRB5_KTNAME=/etc/openldap/krb5-ldap.keytab +c_need="!net net.lo" + +# setting rc_send_sigkill to no. +rc_cgroup_cleanup="NO" + +# If this is yes, we will send sighup to the processes in the cgroup +# immediately after stopsig and sigcont. +rc_send_sighup="YES" + +clock="Any Clock Type" + +clock_systohc="YES" + +rc_timeout_stopsec="90" + diff --git a/tests/format/testfiles/openrc_template b/tests/format/testfiles/openrc_template new file mode 100644 index 0000000..2fb7fd4 --- /dev/null +++ b/tests/format/testfiles/openrc_template @@ -0,0 +1,15 @@ + +# It must be set to yes in this file if you want this functionality. +rc_cgroup_memory_use_hierarchy="YES" + +clock="Any Clock Type" + +clock_systohc="YES" + +!rc_cgroup_mode + +# This is the amount of time in seconds that we delay after sending sigcont +# and optionally sighup, before we optionally send sigkill to all +# processes in the # cgroup. +# The default is 90 seconds. +rc_timeout_stopsec="90" diff --git a/tests/format/testfiles/postfix_original b/tests/format/testfiles/postfix_original new file mode 100644 index 0000000..c4f074b --- /dev/null +++ b/tests/format/testfiles/postfix_original @@ -0,0 +1,34 @@ +#Рабочая директория Postfix. То место, где временно сохраняется +#вся приходящая почта до процесса доставки. +queue_directory = /var/spool/postfix + +#Путь для всех выполняемых программ почтового сервера. +command_directory = /usr/sbin + +#Эта директива указывает postfix какие сети он должен считать +#локальными. Другими словами, компьютеры, соединяющиеся из этих сетей +#будут иметь возможность релея почтовых сообщений. В нашем случае мы +#прописываем следующие сети 127.0.0.0 (для localhost, собственно сам +#сервер), 192.168.10.0 (для всех других компьютеров в нашей внешней сети), +#и 192.168.0.0 (для всех компьютеров во внутренней сети). +# 93.100.239.44 - Алексей Пестриков +# 109.167.151.108 - lautre.ru +mynetworks = 10.0.0.0/8, 80.246.243.18, 95.213.228.194, 94.159.1.246, 109.167.151.108, 80.246.245.82, 80.246.248.234, 93.100.239.44 + +#По умолчанию, Postfix пытается посылать почту в Internet напрямую. В зависимости +#от окружения, в котором функционирует Ваш почтовый сервер, это может быть +#невозможно или нежелательно. Например, Ваша машина может быть отключена от +#Internet-а в нерабочее время, она может быть закрыта файрволлом, Ваш провайдер +#может запрещать пересылку почты в Internet напрямую. В таких случаях Вам +#необходимо настроить Postfix на пересылку писем через другой почтовый сервер +#(relay host). +#Вариант, заключенный в квадратные скобки [], заставляет Postfix +#не предпринимать поиск записей DNS MX. +#по умолчанию: посылать в Internet напрямую +#relayhost = [mail.$mydomain] + + +relay_domains = lists.calculate-linux.org +#Для создания базы используется postmap +transport_maps = hash:/etc/postfix/transport_maps +relay_recipient_maps = hash:/etc/postfix/valid_recipients diff --git a/tests/format/testfiles/postfix_result b/tests/format/testfiles/postfix_result new file mode 100644 index 0000000..70c0b72 --- /dev/null +++ b/tests/format/testfiles/postfix_result @@ -0,0 +1,41 @@ +#Рабочая директория Postfix. То место, где временно сохраняется +#вся приходящая почта до процесса доставки. +queue_directory = /var/spool/postfix + +#Эта директива указывает postfix какие сети он должен считать +#локальными. Другими словами, компьютеры, соединяющиеся из этих сетей +#будут иметь возможность релея почтовых сообщений. В нашем случае мы +#прописываем следующие сети 127.0.0.0 (для localhost, собственно сам +#сервер), 192.168.10.0 (для всех других компьютеров в нашей внешней сети), +#и 192.168.0.0 (для всех компьютеров во внутренней сети). +# 93.100.239.44 - Алексей Пестриков +# 109.167.151.108 - lautre.ru +mynetworks = 10.0.0.0/8, 80.246.243.18, 95.213.228.194, 94.159.1.246, 109.167.151.108, 80.246.245.82, 80.246.248.234, 93.100.239.44 + +#По умолчанию, Postfix пытается посылать почту в Internet напрямую. В зависимости +#от окружения, в котором функционирует Ваш почтовый сервер, это может быть +#невозможно или нежелательно. Например, Ваша машина может быть отключена от +#Internet-а в нерабочее время, она может быть закрыта файрволлом, Ваш провайдер +#может запрещать пересылку почты в Internet напрямую. В таких случаях Вам +#необходимо настроить Postfix на пересылку писем через другой почтовый сервер +#(relay host). +#Вариант, заключенный в квадратные скобки [], заставляет Postfix +#не предпринимать поиск записей DNS MX. +#по умолчанию: посылать в Internet напрямую +#relayhost = [mail.$mydomain] +relay_domains = lists.calculate-linux.org, and something above + +relay_recipient_maps = hash:/etc/postfix/valid_recipient_maps + +chmod = 0644 + +chown = root:root + +mail_owner = postfix + +recipient_maps = ldap:/etc/postfix/ldap-recipient.cf, ldap:/etc/postfix/ldap-recipient-gr.cf + +local_recipient_maps = ldap:/etc/postfix/ldap-recipient.cf, ldap:/etc/postfix/ldap-recipient-gr.cf, ldap:/etc/postfix/ldap-recipient-repl.cf + +unknown_local_recipient_reject_code = 550 + diff --git a/tests/format/testfiles/postfix_template b/tests/format/testfiles/postfix_template new file mode 100644 index 0000000..eb3c139 --- /dev/null +++ b/tests/format/testfiles/postfix_template @@ -0,0 +1,26 @@ +# Little change. +relay_recipient_maps = hash:/etc/postfix/valid_recipient_maps + +chmod=0644 +chown=root:root + +!transport_maps + +#Определяет местоположение всех программ системы postfix. +mail_owner = postfix + +recipient_maps = ldap:/etc/postfix/ldap-recipient.cf, ldap:/etc/postfix/ldap-recipient-gr.cf + +# Если репликация включена но сервер не почтовый релей +local_recipient_maps = ldap:/etc/postfix/ldap-recipient.cf, ldap:/etc/postfix/ldap-recipient-gr.cf, ldap:/etc/postfix/ldap-recipient-repl.cf + +# The unknown_local_recipient_reject_code specifies the SMTP server +# response code when a recipient domain matches $mydestination or +# ${proxy,inet}_interfaces, while $local_recipient_maps is non-empty +# and the recipient address or address local-part is not found. +unknown_local_recipient_reject_code = 550 + +# Значение изменится, комментарии останутся. +relay_domains = lists.calculate-linux.org, and something above + +!command_directory = diff --git a/tests/format/testfiles/procmail_original b/tests/format/testfiles/procmail_original new file mode 100644 index 0000000..83593eb --- /dev/null +++ b/tests/format/testfiles/procmail_original @@ -0,0 +1,29 @@ +# port for HTTP (descriptions, SOAP, media transfer) traffic +port=8200 + +# specify the user account name or uid to run as +user=jmaggard + +# You should compile nfsd into the kernel or add it +# to modules.autoload for this to work properly +vm.dirty_background_ratio = 1 + +vm.dirty_ratio = 3 + +kernel.unprivileged_userns_clone=1 + +# this should be a list of file names to check for when searching for album art +# note: names should be delimited with a forward slash ("/") +album_art_names=Cover.jpg/cover.jpg/AlbumArtSmall.jpg/albumartsmall.jpg/AlbumArt.jpg/albumart.jpg/Album.jpg/album.jpg/Folder.jpg/folder.jpg/Thumb.jpg/thumb.jpg + +# set this to no to disable inotify monitoring to automatically discover new files +# note: the default is yes +inotify=yes + +# set this to yes to enable support for streaming .jpg and .mp3 files to a TiVo supporting HMO +enable_tivo=no + +# set this to strictly adhere to DLNA standards. +# * This will allow server-side downscaling of very large JPEG images, +# which may hurt JPEG serving performance on (at least) Sony DLNA products. +strict_dlna=no diff --git a/tests/format/testfiles/procmail_result b/tests/format/testfiles/procmail_result new file mode 100644 index 0000000..a5e27fb --- /dev/null +++ b/tests/format/testfiles/procmail_result @@ -0,0 +1,31 @@ +# port for HTTP (descriptions, SOAP, media transfer) traffic +port=8200 + +# specify the user account name or uid to run as +user=jmaggard + +# You should compile nfsd into the kernel or add it +# to modules.autoload for this to work properly +vm.dirty_background_ratio=1 + +vm.dirty_ratio=3 + +kernel.unprivileged_userns_clone=0 + +# this should be a list of file names to check for when searching for album art +# note: names should be delimited with a forward slash ("/") +album_art_names=Cover.jpg/cover.jpg/AlbumArtSmall.jpg/albumartsmall.jpg/AlbumArt.jpg/albumart.jpg/Album.jpg/album.jpg/Folder.jpg/folder.jpg/Thumb.jpg/thumb.jpg + +# set this to no to disable inotify monitoring to automatically discover new files +# note: the default is yes +inotify=no + +# set this to yes to enable support for streaming .jpg and .mp3 files to a TiVo supporting HMO +enable_tivo=no + +friendly_name=mike-desktop + +media_dir=PV,/var/calculate/server-data/samba/share + +net.ipv4.icmp_echo_ignore_broadcasts=1 + diff --git a/tests/format/testfiles/procmail_template b/tests/format/testfiles/procmail_template new file mode 100644 index 0000000..57276a1 --- /dev/null +++ b/tests/format/testfiles/procmail_template @@ -0,0 +1,24 @@ + +# note: many clients open several simultaneous connections while streaming +#max_connections=50 +friendly_name=mike-desktop + +!strict_dlna= + +# disable inotify monitoring to automatically discover new files. +inotify=no + +# set this to the directory you want scanned. +# * if you want multiple directories, you can have multiple media_dir= lines +# * if you want to restrict a media_dir to specific content types, you +# can prepend the types, followed by a comma, to the directory: +# + "A" for audio (eg. media_dir=A,/home/jmaggard/Music) +# + "V" for video (eg. media_dir=V,/home/jmaggard/Videos) +# + "P" for images (eg. media_dir=P,/home/jmaggard/Pictures) +# + "PV" for pictures and video (eg. media_dir=PV,/home/jmaggard/digital_camera) +media_dir=PV,/var/calculate/server-data/samba/share + +kernel.unprivileged_userns_clone=0 + +# Ignore ICMP broadcasts +net.ipv4.icmp_echo_ignore_broadcasts = 1 diff --git a/tests/format/testfiles/proftpd_original.conf b/tests/format/testfiles/proftpd_original.conf new file mode 100644 index 0000000..77c29f9 --- /dev/null +++ b/tests/format/testfiles/proftpd_original.conf @@ -0,0 +1,63 @@ +# This is a basic ProFTPD configuration file (rename it to +# 'proftpd.conf' for actual use. It establishes a single server +# and a single anonymous login. It assumes that you have a user/group +# "nobody" and "ftp" for normal operation and anon. + +ServerName "ProFTPD Default Installation" +ServerType standalone +DefaultServer on + +# Port 21 is the standard FTP port. +Port 21 + +# Umask 022 is a good standard umask to prevent new dirs and files +# from being group and world writable. +Umask 022 + +# To prevent DoS attacks, set the maximum number of child processes +# to 30. If you need to allow more than 30 concurrent connections +# at once, simply increase this value. Note that this ONLY works +# in standalone mode, in inetd mode you should use an inetd server +# that allows you to limit maximum number of processes per service +# (such as xinetd). +MaxInstances 30 + +# Set the user and group under which the server will run. +User nobody +Group nogroup + +# To cause every FTP user to be "jailed" (chrooted) into their home +# directory, uncomment this line. +#DefaultRoot ~ + +# Normally, we want files to be overwriteable. + + AllowOverwrite on + + +# A basic anonymous configuration, no upload directories. If you do not +# want anonymous users, simply delete this entire section. + + User ftp + Group ftp + + # We want clients to be able to login with "anonymous" as well as "ftp" + UserAlias anonymous ftp + + # Limit the maximum number of anonymous logins + MaxClients 10 + + # We want 'welcome.msg' displayed at login, and '.message' displayed + # in each newly chdired directory. + DisplayLogin welcome.msg + DisplayFirstChdir .message + + + IgnoreHidden on + + + # Limit WRITE everywhere in the anonymous chroot + + DenyAll + + diff --git a/tests/format/testfiles/proftpd_result.conf b/tests/format/testfiles/proftpd_result.conf new file mode 100644 index 0000000..98a2471 --- /dev/null +++ b/tests/format/testfiles/proftpd_result.conf @@ -0,0 +1,84 @@ + +# This is a basic ProFTPD configuration file (rename it to +# 'proftpd.conf' for actual use. It establishes a single server +# and a single anonymous login. It assumes that you have a user/group +# "nobody" and "ftp" for normal operation and anon. +ServerName "ProFTPD Default Installation" +ServerType standalone +DefaultServer on + +# Port 21 is the standard FTP port. +Port 21 + +# Umask 022 is a good standard umask to prevent new dirs and files +# from being group and world writable. +Umask 022 + +# To prevent DoS attacks, set the maximum number of child processes +# to 30. If you need to allow more than 30 concurrent connections +# at once, simply increase this value. Note that this ONLY works +# in standalone mode, in inetd mode you should use an inetd server +# that allows you to limit maximum number of processes per service +# (such as xinetd). +MaxInstances 30 + +# Set the user and group under which the server will run. +User nobody +Group nogroup + +# To cause every FTP user to be "jailed" (chrooted) into their home +# directory, uncomment this line. +#DefaultRoot ~ +# Normally, we want files to be overwriteable. + + AllowOverwrite off + + + +# A basic anonymous configuration, no upload directories. If you do not +# want anonymous users, simply delete this entire section. + + User ftp + Group ftp + + # We want clients to be able to login with "anonymous" as well as "ftp" + UserAlias anonymous ftp + + + DenyAll + IgnoreHidden on + + + + AllowAll + IgnoreHidden on + + + + + DenyAll + + + + + LangDefault it_IT + + + + User guest + Group empty + AnonRequirePassword on + + AllowAll + + + + Order allow,deny + Allow from 10.0.0. + Deny from all + + + + + UseEncoding utf8 cp1251 + diff --git a/tests/format/testfiles/proftpd_template.conf b/tests/format/testfiles/proftpd_template.conf new file mode 100644 index 0000000..4961f8a --- /dev/null +++ b/tests/format/testfiles/proftpd_template.conf @@ -0,0 +1,74 @@ +# This is a calculate template file, created for test. +# All comments in this file are ignored during the template merging. +# +# Let's delete directive from Anonymous block. + + + !DenyAll + + + +# To add some new directives in Anonymous block. + + !MaxClients + + + DenyAll + IgnoreHidden on + + + + AllowAll + IgnoreHidden on + + + + +# Let's try to change the value of AllowOverwrite directive in Directory context. + + AllowOverwrite off + + +# We can add deleted directive again. + + + DenyAll + + + + + LangDefault it_IT + + +# Now, we are going to add new Anonymous block. + + User guest + Group empty + AnonRequirePassword on + + + AllowAll + + + # We want to deny write access from all except trusted host. + + Order allow,deny + Allow from 10.0.0. + Deny from all + + + +# We can delete parameters using action marks for groups of directives. + + DisplayLogin welcome.msg + # We can write only directive key without any parameters to delete them. + # For this operation action mark is always needed. + !DisplayFirstChdir + + IgnoreHidden on + + + + + UseEncoding utf8 cp1251 + diff --git a/tests/format/testfiles/samba_original b/tests/format/testfiles/samba_original new file mode 100644 index 0000000..1fb86dc --- /dev/null +++ b/tests/format/testfiles/samba_original @@ -0,0 +1,24 @@ +#=============================== Global Settings ============================== +[global] + server role = standalone server + hosts allow = 192.168.1. 192.168.2. 127. + guest account = pcguest + log file = /var/log/samba/log.%m + +# DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names +# via DNS nslookups. The default is NO. + dns proxy = no + +#============================== Share Definitions ============================= +[homes] + comment = Home Directories + browseable = no + writable = yes + +# Un-comment the following and create the netlogon directory for Domain Logons +[netlogon] + comment = Network Logon Service + path = /var/lib/samba/netlogon + guest ok = yes + writable = no + share modes = no diff --git a/tests/format/testfiles/samba_result b/tests/format/testfiles/samba_result new file mode 100644 index 0000000..1a410d0 --- /dev/null +++ b/tests/format/testfiles/samba_result @@ -0,0 +1,49 @@ +#=============================== Global Settings ============================== +[global] + server role = standalone server + hosts allow = 192.168.1. 192.168.2. 127. + log file = /var/log/samba/log.%m + workgroup = group name + netbios name = strange name + server string = Calculate Directory Server + directory mask = 0755 + directory security mask = 0755 + force directory security mode = 0755 + security mask = 0644 + force security mode = 0644 + create mask = 0644 + enable privileges = yes +#============================== Share Definitions ============================= +[homes] + path = /home/user/somewhere + comment = Home Directories + valid users = %U + read only = No + browseable = No +# Un-comment the following and create the netlogon directory for Domain Logons +[netlogon] + path = /var/lib/samba/netlogon + guest ok = yes + share modes = no +[tmp] + comment = Public Stuff + path = /home/samba + public = yes + writable = no + printable = no + write list = @staff +[unix] + path = /home/path + browseable = No + writable = yes + directory mask = 0777 + directory security mask = 0777 + force directory security mode = 0 + create mask = 0777 + security mask = 0777 + force security mode = 0 + force user = %U + valid users = %U + hide unreadable = no + root preexec = /usr/lib/calculate/calculate-server/bin/execserv -s --login %U + root postexec = /usr/lib/calculate/calculate-server/bin/execserv -s --logout %U diff --git a/tests/format/testfiles/samba_template b/tests/format/testfiles/samba_template new file mode 100644 index 0000000..db10760 --- /dev/null +++ b/tests/format/testfiles/samba_template @@ -0,0 +1,69 @@ +[global] + # Имя сервера + workgroup = group name + netbios name = strange name + server string = Calculate Directory Server + + #маска создаваемых директорий для Windows и cifs-клиентов + directory mask = 0755 + #маска для изменения прав на директории для cifs-клиентов + directory security mask = 0755 + #маска создаваемых директорий для smbfs-клиентов + force directory security mode = 0755 + #ограничение прав файлов при создании и модификации + #для cifs и smbfs клиентов + security mask = 0644 + force security mode = 0644 + #права файлов на создаваемые и модифицируемые файлы из Windows + create mask = 0644 + + # добавление админских привилегий в домене группе Domain Admins + enable privileges = yes + + !guest account = + !dns proxy + +# A publicly accessible directory, but read only, except for people in +# the "staff" group +[tmp] + comment = Public Stuff + path = /home/samba + public = yes + writable = no + printable = no + write list = @staff + +[netlogon] + comment = Logon Service + share modes = no + +[unix] + path = /home/path + browseable = No + writable = yes + directory mask = 0777 + directory security mask = 0777 + force directory security mode = 0 + create mask = 0777 + +[netlogon] + + !writable = yes + !comment + +[-homes] + # New comment + path = /home/user/somewhere + comment = Home Directories + valid users = %U + read only = No + browseable = No + +[unix] + security mask = 0777 + force security mode = 0 + force user = %U + valid users = %U + hide unreadable = no + root preexec = /usr/lib/calculate/calculate-server/bin/execserv -s --login %U + root postexec = /usr/lib/calculate/calculate-server/bin/execserv -s --logout %U diff --git a/tests/format/testfiles/xml_gconf_original.xml b/tests/format/testfiles/xml_gconf_original.xml new file mode 100644 index 0000000..e3fdc0c --- /dev/null +++ b/tests/format/testfiles/xml_gconf_original.xml @@ -0,0 +1,34 @@ + + + + + + + + pulsesrc + + + + + + + + + + + True if the command used to handle this type of URL should be run in a terminal. + + + + + + + + + + distributor + + + + + diff --git a/tests/format/testfiles/xml_gconf_result.xml b/tests/format/testfiles/xml_gconf_result.xml new file mode 100644 index 0000000..6b9c03b --- /dev/null +++ b/tests/format/testfiles/xml_gconf_result.xml @@ -0,0 +1,32 @@ + + + + + + + + Something I can never have. + + + True if the command used to handle this type of URL should be run in a terminal. + + + + + + + + + + distributor + + + + + + + + + + + diff --git a/tests/format/testfiles/xml_gconf_template.xml b/tests/format/testfiles/xml_gconf_template.xml new file mode 100644 index 0000000..8b1ac05 --- /dev/null +++ b/tests/format/testfiles/xml_gconf_template.xml @@ -0,0 +1,36 @@ + + + + + + + + pulsesrc + + + + + + + + + + + + + + + + + + + Something I can never have. + + + True if the command used to handle this type of URL should be run in a terminal. + + + + + + diff --git a/tests/format/testfiles/xml_xfce_original.xml b/tests/format/testfiles/xml_xfce_original.xml new file mode 100644 index 0000000..69399da --- /dev/null +++ b/tests/format/testfiles/xml_xfce_original.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/format/testfiles/xml_xfce_result.xml b/tests/format/testfiles/xml_xfce_result.xml new file mode 100644 index 0000000..06d7a9d --- /dev/null +++ b/tests/format/testfiles/xml_xfce_result.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/tests/format/testfiles/xml_xfce_template.xml b/tests/format/testfiles/xml_xfce_template.xml new file mode 100644 index 0000000..8a6b6ab --- /dev/null +++ b/tests/format/testfiles/xml_xfce_template.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/utils/test_files.py b/tests/utils/test_files.py new file mode 100644 index 0000000..a4fe478 --- /dev/null +++ b/tests/utils/test_files.py @@ -0,0 +1,79 @@ +import pytest +from calculate.utils.files import Process +from subprocess import run, PIPE + + +@pytest.mark.files +class TestUtils(): + def test_if_single_correct_command_executed_using_Process_object__it_successfully_executes(self): + try: + ls_process = Process('ls', '-a', cwd='/') + ls_result = run('ls -a', shell=True, stdout=PIPE, cwd='/') + except Exception as error: + print('error:', str(error)) + assert False + + output = ls_process.read().strip() + result = ls_result.stdout.decode().strip() + assert output == result + + def test_if_pipe_is_executed_using_Process_object__it_has_successfully_executed(self): + try: + pipe = Process('grep', 'VGA', + stdin=Process('/usr/sbin/lspci') + ) + pipe_result = run('/usr/sbin/lspci | grep "VGA"', shell=True, stdout=PIPE) + except Exception as error: + print('error:', str(error)) + assert False + + output = pipe.read().strip() + result = pipe_result.stdout.decode().strip() + assert output == result + + def test_if_a_pipe_Process_object_uses_for_several_writes_without_readings__it_successfully_executes(self): + input_lines = ['KEY: important line.\n', + 'NONE: not important line.\n', + 'NONE: nothing interesting.\n', + 'KEY: another line.\n'] + + output_text = 'KEY: important line.\nKEY: another line.\n' + + try: + pipe = Process('grep', 'KEY', + stdin=Process('cat', stdin=PIPE) + ) + except Exception as error: + print('error:', str(error)) + assert False + + for line in input_lines: + pipe.write(line) + output = pipe.read() + assert output == output_text + + def test_if_a_pipe_Process_object_uses_for_several_writes__it_successfully_executes_even_after_read(self): + input_lines = ['KEY: important line.\n', + 'NONE: not important line.\n', + 'NONE: nothing interesting.\n', + 'KEY: another line.\n'] + + output_lines = ['KEY: important line.\n', + 'KEY: another line.\n'] + + try: + pipe = Process('grep', 'KEY', + stdin=Process('cat', stdin=PIPE) + ) + except Exception as error: + print('error:', str(error)) + assert False + + output = [] + for line in input_lines: + pipe.write(line) + output_line = pipe.read() + + if output_line: + output.append(output_line) + assert output == output_lines diff --git a/tests/vars/__init__.py b/tests/vars/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/vars/test_namespace.py b/tests/vars/test_namespace.py new file mode 100644 index 0000000..b13dbdc --- /dev/null +++ b/tests/vars/test_namespace.py @@ -0,0 +1,14 @@ +import pytest +from calculate.vars.datavars import Namespace + +@pytest.mark.vars +class TestNamespace: + def test_init_empty_namespace(self): + ns = Namespace(varPath=None) + assert ns.varPath is None + + def test_init_default_path(self): + ns = Namespace() + assert ns.varPath.startswith("/usr/lib") and \ + ns.varPath.endswith("calculate/vars") +