From 81951b323083eb91e0351cb409d83e6b6c0f3473 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 3 Feb 2022 20:28:06 +0100 Subject: [PATCH 01/32] chore: ignore venv --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 58d609bb..723dc685 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ __pycache__ /yamllint.egg-info /build /.eggs +.venv +venv From b858cbbb8224780855036f0f234bbcb73047b050 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 3 Feb 2022 20:33:19 +0100 Subject: [PATCH 02/32] refactor: moved formater inside another file --- yamllint/cli.py | 106 +------------------------------------------ yamllint/format.py | 111 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 105 deletions(-) create mode 100644 yamllint/format.py diff --git a/yamllint/cli.py b/yamllint/cli.py index 4d943424..4d3a53d7 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -20,13 +20,13 @@ import io import locale import os -import platform import sys from yamllint import APP_DESCRIPTION, APP_NAME, APP_VERSION from yamllint import linter from yamllint.config import YamlLintConfig, YamlLintConfigError from yamllint.linter import PROBLEM_LEVELS +from yamllint.format import show_problems def find_files_recursively(items, conf): @@ -41,110 +41,6 @@ def find_files_recursively(items, conf): yield item -def supports_color(): - supported_platform = not (platform.system() == 'Windows' and not - ('ANSICON' in os.environ or - ('TERM' in os.environ and - os.environ['TERM'] == 'ANSI'))) - return (supported_platform and - hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()) - - -class Format(object): - @staticmethod - def parsable(problem, filename): - return ('%(file)s:%(line)s:%(column)s: [%(level)s] %(message)s' % - {'file': filename, - 'line': problem.line, - 'column': problem.column, - 'level': problem.level, - 'message': problem.message}) - - @staticmethod - def standard(problem, filename): - line = ' %d:%d' % (problem.line, problem.column) - line += max(12 - len(line), 0) * ' ' - line += problem.level - line += max(21 - len(line), 0) * ' ' - line += problem.desc - if problem.rule: - line += ' (%s)' % problem.rule - return line - - @staticmethod - def standard_color(problem, filename): - line = ' \033[2m%d:%d\033[0m' % (problem.line, problem.column) - line += max(20 - len(line), 0) * ' ' - if problem.level == 'warning': - line += '\033[33m%s\033[0m' % problem.level - else: - line += '\033[31m%s\033[0m' % problem.level - line += max(38 - len(line), 0) * ' ' - line += problem.desc - if problem.rule: - line += ' \033[2m(%s)\033[0m' % problem.rule - return line - - @staticmethod - def github(problem, filename): - line = '::' - line += problem.level - line += ' file=' + filename + ',' - line += 'line=' + format(problem.line) + ',' - line += 'col=' + format(problem.column) - line += '::' - line += format(problem.line) - line += ':' - line += format(problem.column) - line += ' ' - if problem.rule: - line += '[' + problem.rule + '] ' - line += problem.desc - return line - - -def show_problems(problems, file, args_format, no_warn): - max_level = 0 - first = True - - if args_format == 'auto': - if ('GITHUB_ACTIONS' in os.environ and - 'GITHUB_WORKFLOW' in os.environ): - args_format = 'github' - elif supports_color(): - args_format = 'colored' - - for problem in problems: - max_level = max(max_level, PROBLEM_LEVELS[problem.level]) - if no_warn and (problem.level != 'error'): - continue - if args_format == 'parsable': - print(Format.parsable(problem, file)) - elif args_format == 'github': - if first: - print('::group::%s' % file) - first = False - print(Format.github(problem, file)) - elif args_format == 'colored': - if first: - print('\033[4m%s\033[0m' % file) - first = False - print(Format.standard_color(problem, file)) - else: - if first: - print(file) - first = False - print(Format.standard(problem, file)) - - if not first and args_format == 'github': - print('::endgroup::') - - if not first and args_format != 'parsable': - print('') - - return max_level - - def run(argv=None): parser = argparse.ArgumentParser(prog=APP_NAME, description=APP_DESCRIPTION) diff --git a/yamllint/format.py b/yamllint/format.py new file mode 100644 index 00000000..6df0e6fd --- /dev/null +++ b/yamllint/format.py @@ -0,0 +1,111 @@ + +from __future__ import print_function + +import os +import platform +import sys + +from yamllint.linter import PROBLEM_LEVELS + + +def supports_color(): + supported_platform = not (platform.system() == 'Windows' and not + ('ANSICON' in os.environ or + ('TERM' in os.environ and + os.environ['TERM'] == 'ANSI'))) + return (supported_platform and + hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()) + +class Format(object): + @staticmethod + def parsable(problem, filename): + return ('%(file)s:%(line)s:%(column)s: [%(level)s] %(message)s' % + {'file': filename, + 'line': problem.line, + 'column': problem.column, + 'level': problem.level, + 'message': problem.message}) + + @staticmethod + def standard(problem, filename): + line = ' %d:%d' % (problem.line, problem.column) + line += max(12 - len(line), 0) * ' ' + line += problem.level + line += max(21 - len(line), 0) * ' ' + line += problem.desc + if problem.rule: + line += ' (%s)' % problem.rule + return line + + @staticmethod + def standard_color(problem, filename): + line = ' \033[2m%d:%d\033[0m' % (problem.line, problem.column) + line += max(20 - len(line), 0) * ' ' + if problem.level == 'warning': + line += '\033[33m%s\033[0m' % problem.level + else: + line += '\033[31m%s\033[0m' % problem.level + line += max(38 - len(line), 0) * ' ' + line += problem.desc + if problem.rule: + line += ' \033[2m(%s)\033[0m' % problem.rule + return line + + @staticmethod + def github(problem, filename): + line = '::' + line += problem.level + line += ' file=' + filename + ',' + line += 'line=' + format(problem.line) + ',' + line += 'col=' + format(problem.column) + line += '::' + line += format(problem.line) + line += ':' + line += format(problem.column) + line += ' ' + if problem.rule: + line += '[' + problem.rule + '] ' + line += problem.desc + return line + + +def show_problems(problems, file, args_format, no_warn): + max_level = 0 + first = True + + if args_format == 'auto': + if ('GITHUB_ACTIONS' in os.environ and + 'GITHUB_WORKFLOW' in os.environ): + args_format = 'github' + elif supports_color(): + args_format = 'colored' + + for problem in problems: + max_level = max(max_level, PROBLEM_LEVELS[problem.level]) + if no_warn and (problem.level != 'error'): + continue + if args_format == 'parsable': + print(Format.parsable(problem, file)) + elif args_format == 'github': + if first: + print('::group::%s' % file) + first = False + print(Format.github(problem, file)) + elif args_format == 'colored': + if first: + print('\033[4m%s\033[0m' % file) + first = False + print(Format.standard_color(problem, file)) + else: + if first: + print(file) + first = False + print(Format.standard(problem, file)) + + if not first and args_format == 'github': + print('::endgroup::') + + if not first and args_format != 'parsable': + print('') + + return max_level From 3601817dd2b9ecb272d5e52808ae75601345b8bc Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 3 Feb 2022 20:59:27 +0100 Subject: [PATCH 03/32] refactor: moved output at the end of the tests --- yamllint/cli.py | 21 ++++++++++++--------- yamllint/format.py | 10 ++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/yamllint/cli.py b/yamllint/cli.py index 4d3a53d7..93cecc5a 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -26,7 +26,7 @@ from yamllint import linter from yamllint.config import YamlLintConfig, YamlLintConfigError from yamllint.linter import PROBLEM_LEVELS -from yamllint.format import show_problems +from yamllint.format import show_all_problems def find_files_recursively(items, conf): @@ -107,7 +107,8 @@ def run(argv=None): if conf.locale is not None: locale.setlocale(locale.LC_ALL, conf.locale) - max_level = 0 + # problems dict: {file: problems} + all_problems = dict() for file in find_files_recursively(args.files, conf): filepath = file[2:] if file.startswith('./') else file @@ -117,20 +118,22 @@ def run(argv=None): except EnvironmentError as e: print(e, file=sys.stderr) sys.exit(-1) - prob_level = show_problems(problems, file, args_format=args.format, - no_warn=args.no_warnings) - max_level = max(max_level, prob_level) + all_problems[file] = problems - # read yaml from stdin if args.stdin: + # read yaml from stdin try: problems = linter.run(sys.stdin, conf, '') except EnvironmentError as e: print(e, file=sys.stderr) sys.exit(-1) - prob_level = show_problems(problems, 'stdin', args_format=args.format, - no_warn=args.no_warnings) - max_level = max(max_level, prob_level) + all_problems['stdin'] = problems + + max_level = show_all_problems( + all_problems, + args_format=args.format, + no_warn=args.no_warnings + ) if max_level == PROBLEM_LEVELS['error']: return_code = 1 diff --git a/yamllint/format.py b/yamllint/format.py index 6df0e6fd..ad32c814 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -16,6 +16,7 @@ def supports_color(): return (supported_platform and hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()) + class Format(object): @staticmethod def parsable(problem, filename): @@ -109,3 +110,12 @@ def show_problems(problems, file, args_format, no_warn): print('') return max_level + + +def show_all_problems(all_problems, args_format, no_warn): + """Print all problems, return the max level.""" + max_level = 0 + + for file, problem in all_problems.items(): + curr_level = show_problems(problem, file, args_format, no_warn) + max_level = max(curr_level, max_level) From 46a12a83a8d183e9f0d49363373cd7a939a7bc55 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 3 Feb 2022 21:08:53 +0100 Subject: [PATCH 04/32] fix: missed return statement --- yamllint/format.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/yamllint/format.py b/yamllint/format.py index ad32c814..8e601c78 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -119,3 +119,5 @@ def show_all_problems(all_problems, args_format, no_warn): for file, problem in all_problems.items(): curr_level = show_problems(problem, file, args_format, no_warn) max_level = max(curr_level, max_level) + + return max_level From c4463c706f5ed826d8c96d797a4d20a2604a2d5c Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 3 Feb 2022 21:23:06 +0100 Subject: [PATCH 05/32] feat: max_level as separate function --- yamllint/format.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/yamllint/format.py b/yamllint/format.py index 8e601c78..e98103a4 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -71,7 +71,6 @@ def github(problem, filename): def show_problems(problems, file, args_format, no_warn): - max_level = 0 first = True if args_format == 'auto': @@ -82,7 +81,6 @@ def show_problems(problems, file, args_format, no_warn): args_format = 'colored' for problem in problems: - max_level = max(max_level, PROBLEM_LEVELS[problem.level]) if no_warn and (problem.level != 'error'): continue if args_format == 'parsable': @@ -109,15 +107,16 @@ def show_problems(problems, file, args_format, no_warn): if not first and args_format != 'parsable': print('') - return max_level + +def max_level(problems): + """Return the max level of all problems.""" + return max(PROBLEM_LEVELS[problem.level] for problem in problems) def show_all_problems(all_problems, args_format, no_warn): """Print all problems, return the max level.""" - max_level = 0 for file, problem in all_problems.items(): - curr_level = show_problems(problem, file, args_format, no_warn) - max_level = max(curr_level, max_level) + show_problems(problem, file, args_format, no_warn) - return max_level + return max_level(all_problems) From 6217241fec303361fa68d6ed93e666ae6e5d6799 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 3 Feb 2022 22:20:18 +0100 Subject: [PATCH 06/32] refactor: use object inheritance for the formaters --- yamllint/cli.py | 4 +- yamllint/format.py | 253 ++++++++++++++++++++++++++++++++------------- 2 files changed, 181 insertions(+), 76 deletions(-) diff --git a/yamllint/cli.py b/yamllint/cli.py index 93cecc5a..617064a1 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -118,7 +118,7 @@ def run(argv=None): except EnvironmentError as e: print(e, file=sys.stderr) sys.exit(-1) - all_problems[file] = problems + all_problems[file] = [pb for pb in problems if pb] if args.stdin: # read yaml from stdin @@ -127,7 +127,7 @@ def run(argv=None): except EnvironmentError as e: print(e, file=sys.stderr) sys.exit(-1) - all_problems['stdin'] = problems + all_problems['stdin'] = [pb for pb in problems if pb] max_level = show_all_problems( all_problems, diff --git a/yamllint/format.py b/yamllint/format.py index e98103a4..4ddcc4c6 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -17,29 +17,149 @@ def supports_color(): hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()) -class Format(object): - @staticmethod - def parsable(problem, filename): - return ('%(file)s:%(line)s:%(column)s: [%(level)s] %(message)s' % - {'file': filename, - 'line': problem.line, - 'column': problem.column, - 'level': problem.level, - 'message': problem.message}) - - @staticmethod - def standard(problem, filename): - line = ' %d:%d' % (problem.line, problem.column) - line += max(12 - len(line), 0) * ' ' +def run_on_gh(): + """Return if the currnet job is on github.""" + return 'GITHUB_ACTIONS' in os.environ and 'GITHUB_WORKFLOW' in os.environ + + +class Formater(object): + """Any formater.""" + # the formater name + name = '' + + @classmethod + def get_formater(cls, name, no_warn): + """Return a formater instance.""" + + if name == 'auto': + if run_on_gh(): + name = 'github' + elif supports_color(): + name = 'colored' + else: + name = 'standard' + + for formater in cls.__subclasses__(): + if name == formater.name: + return formater(no_warn) + raise ValueError('unknown formater: %s' % name) + + def __init__(self, no_warn): + """Setup the formater.""" + self.no_warn = no_warn + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + raise NotImplementedError() + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + raise NotImplementedError() + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + raise NotImplementedError() + + +class ParsableFormater(Formater): + """The parsable formater.""" + name = 'parsable' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '' + for file, problems in all_problems.items(): + string += self.show_problems_for_file(problems, file) + return string + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + string = '' + for problem in problems: + string += self.show_problem(problem, file) + return string + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + if self.no_warn and (problem.level != 'error'): + return '' + return ( + '%(file)s:%(line)s:%(column)s: [%(level)s] %(message)s\n' % + { + 'file': file, + 'line': problem.line, + 'column': problem.column, + 'level': problem.level, + 'message': problem.message + } + ) + + +class GithubFormater(Formater): + """The parsable formater.""" + name = 'github' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '' + for file, problems in all_problems.items(): + string += self.show_problems_for_file(problems, file) + return string + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + string = '::group::%s\n' % file + for problem in problems: + string += self.show_problem(problem, file) + if string == '::group::%s\n' % file: + return '' + return string + '::endgroup::\n\n' + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + if self.no_warn and (problem.level != 'error'): + return '' + line = '::' line += problem.level - line += max(21 - len(line), 0) * ' ' - line += problem.desc + line += ' file=' + file + ',' + line += 'line=' + format(problem.line) + ',' + line += 'col=' + format(problem.column) + line += '::' + line += format(problem.line) + line += ':' + line += format(problem.column) + line += ' ' if problem.rule: - line += ' (%s)' % problem.rule + line += '[' + problem.rule + '] ' + line += problem.desc + line += '\n' return line - @staticmethod - def standard_color(problem, filename): + +class ColoredFormater(Formater): + """The parsable formater.""" + name = 'colored' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '' + for file, problems in all_problems.items(): + string += self.show_problems_for_file(problems, file) + return string + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + string = '\033[4m%s\033[0m\n' % file + for problem in problems: + string += self.show_problem(problem, file) + if string == '\033[4m%s\033[0m\n' % file: + return '' + return string + '\n' + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + if self.no_warn and (problem.level != 'error'): + return '' line = ' \033[2m%d:%d\033[0m' % (problem.line, problem.column) line += max(20 - len(line), 0) * ' ' if problem.level == 'warning': @@ -50,73 +170,58 @@ def standard_color(problem, filename): line += problem.desc if problem.rule: line += ' \033[2m(%s)\033[0m' % problem.rule + line += '\n' return line - @staticmethod - def github(problem, filename): - line = '::' + +class StandardFormater(Formater): + """The parsable formater.""" + name = 'standard' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '' + for file, problems in all_problems.items(): + string += self.show_problems_for_file(problems, file) + return string + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + string = file + '\n' + for problem in problems: + string += self.show_problem(problem, file) + if string == file + '\n': + return '' + return string + '\n' + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + if self.no_warn and (problem.level != 'error'): + return '' + line = ' %d:%d' % (problem.line, problem.column) + line += max(12 - len(line), 0) * ' ' line += problem.level - line += ' file=' + filename + ',' - line += 'line=' + format(problem.line) + ',' - line += 'col=' + format(problem.column) - line += '::' - line += format(problem.line) - line += ':' - line += format(problem.column) - line += ' ' - if problem.rule: - line += '[' + problem.rule + '] ' + line += max(21 - len(line), 0) * ' ' line += problem.desc + if problem.rule: + line += ' (%s)' % problem.rule + line += '\n' return line -def show_problems(problems, file, args_format, no_warn): - first = True - - if args_format == 'auto': - if ('GITHUB_ACTIONS' in os.environ and - 'GITHUB_WORKFLOW' in os.environ): - args_format = 'github' - elif supports_color(): - args_format = 'colored' - - for problem in problems: - if no_warn and (problem.level != 'error'): - continue - if args_format == 'parsable': - print(Format.parsable(problem, file)) - elif args_format == 'github': - if first: - print('::group::%s' % file) - first = False - print(Format.github(problem, file)) - elif args_format == 'colored': - if first: - print('\033[4m%s\033[0m' % file) - first = False - print(Format.standard_color(problem, file)) - else: - if first: - print(file) - first = False - print(Format.standard(problem, file)) - - if not first and args_format == 'github': - print('::endgroup::') - - if not first and args_format != 'parsable': - print('') - - -def max_level(problems): +def max_level(all_problems): """Return the max level of all problems.""" - return max(PROBLEM_LEVELS[problem.level] for problem in problems) + all_levels = [problem.level for problems in all_problems.values() for problem in problems] + if all_levels: + return max(map(lambda x: PROBLEM_LEVELS[x], all_levels)) + return 0 def show_all_problems(all_problems, args_format, no_warn): """Print all problems, return the max level.""" - for file, problem in all_problems.items(): - show_problems(problem, file, args_format, no_warn) + fmt = Formater.get_formater(args_format, no_warn) + + print(fmt.show_problems_for_all_files(all_problems), end='') return max_level(all_problems) From fbf8108b974111b02fd359fa0dca191ff0ed7097 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 3 Feb 2022 22:35:56 +0100 Subject: [PATCH 07/32] feat: json formater --- yamllint/cli.py | 2 +- yamllint/format.py | 21 +++++++++++++++++++++ yamllint/linter.py | 11 +++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/yamllint/cli.py b/yamllint/cli.py index 617064a1..e3b75abb 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -59,7 +59,7 @@ def run(argv=None): help='custom configuration (as YAML source)') parser.add_argument('-f', '--format', choices=('parsable', 'standard', 'colored', 'github', - 'auto'), + 'json', 'auto'), default='auto', help='format for parsing output') parser.add_argument('-s', '--strict', action='store_true', diff --git a/yamllint/format.py b/yamllint/format.py index 4ddcc4c6..07663f4c 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -4,6 +4,7 @@ import os import platform import sys +import json from yamllint.linter import PROBLEM_LEVELS @@ -209,6 +210,26 @@ def show_problem(self, problem, file): return line +class JSONFormater(Formater): + """The parsable formater.""" + name = 'json' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + lst = [] + for k, v in all_problems.items(): + lst += self.show_problems_for_file(v, k) + return json.dumps(lst, indent=4) + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + return list(map(self.show_problem, problems, [file] * len(problems))) + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + return {**problem.dict, "file": file} + + def max_level(all_problems): """Return the max level of all problems.""" all_levels = [problem.level for problems in all_problems.values() for problem in problems] diff --git a/yamllint/linter.py b/yamllint/linter.py index 7e0fb550..c35fc551 100644 --- a/yamllint/linter.py +++ b/yamllint/linter.py @@ -62,6 +62,17 @@ def __lt__(self, other): def __repr__(self): return '%d:%d: %s' % (self.line, self.column, self.message) + @property + def dict(self): + """Return self as a dictionary.""" + return { + "line": self.line, + "column": self.column, + "desc": self.desc, + "rule": self.rule, + "level": self.level, + } + def get_cosmetic_problems(buffer, conf, filepath): rules = conf.enabled_rules(filepath) From 805b9a9739252b676f0ce8fd28b0a21ef4295591 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 3 Feb 2022 23:09:27 +0100 Subject: [PATCH 08/32] feat: junitxml formater --- yamllint/cli.py | 2 +- yamllint/format.py | 58 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/yamllint/cli.py b/yamllint/cli.py index e3b75abb..471a2e0a 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -59,7 +59,7 @@ def run(argv=None): help='custom configuration (as YAML source)') parser.add_argument('-f', '--format', choices=('parsable', 'standard', 'colored', 'github', - 'json', 'auto'), + 'json', 'junitxml', 'auto'), default='auto', help='format for parsing output') parser.add_argument('-s', '--strict', action='store_true', diff --git a/yamllint/format.py b/yamllint/format.py index 07663f4c..98919ab0 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -5,6 +5,7 @@ import platform import sys import json +import datetime from yamllint.linter import PROBLEM_LEVELS @@ -23,6 +24,16 @@ def run_on_gh(): return 'GITHUB_ACTIONS' in os.environ and 'GITHUB_WORKFLOW' in os.environ +def escape_xml(text): + """Escape text for XML.""" + text = text.replace('&', '&') + text = text.replace('<', '<') + text = text.replace('>', '>') + text = text.replace('"', '"') + text = text.replace('"', ''') + return text + + class Formater(object): """Any formater.""" # the formater name @@ -219,7 +230,52 @@ def show_problems_for_all_files(self, all_problems): lst = [] for k, v in all_problems.items(): lst += self.show_problems_for_file(v, k) - return json.dumps(lst, indent=4) + return json.dumps(lst, indent=4) + '\n' + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + return list(map(self.show_problem, problems, [file] * len(problems))) + + def show_problem(self, problem, file): + """Show all problems of a specific file.""" + return {**problem.dict, "file": file} + + +class JunitFormater(Formater): + """The parsable formater.""" + name = 'junitxml' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + string = '\n\n' + + errors = warnings = 0 + lst = [] + for k, v in all_problems.items(): + lst += self.show_problems_for_file(v, k) + + lines = [] + for item in lst: + if item['level'] is None: + continue + elif item['level'] == 'warning': + warnings += 1 + to_append = '<\/failure><\/testcase>' # noqa + elif item['level'] == 'error': + errors += 1 + to_append = '<\/error><\/testcase>' # noqa + lines.append(' ' * 8 + to_append % ( + item['file'], + item['line'], + item['column'], + item['rule'], + escape_xml(item['desc'])) + ) + + string += ' '*4 + '\n' % (errors, warnings, errors + warnings, datetime.datetime.now().isoformat(), platform.node()) # noqa + string += '\n'.join(lines) + '\n' + string += ' \n\n' + return string def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" From 6a06e8d5125c6625bc058b1971aefb115f7e75dd Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Sat, 5 Feb 2022 10:34:34 +0100 Subject: [PATCH 09/32] feat: changed json output file name to path https://github.com/adrienverge/yamllint/pull/442#issuecomment-1030572919 --- yamllint/format.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yamllint/format.py b/yamllint/format.py index 98919ab0..c5218cfe 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -238,7 +238,7 @@ def show_problems_for_file(self, problems, file): def show_problem(self, problem, file): """Show all problems of a specific file.""" - return {**problem.dict, "file": file} + return {**problem.dict, "path": file} class JunitFormater(Formater): From 2839489337b4d723225bcf8f0d983848f6fbd1ca Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Sat, 5 Feb 2022 11:05:38 +0100 Subject: [PATCH 10/32] typo --- yamllint/format.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yamllint/format.py b/yamllint/format.py index c5218cfe..c2bbde15 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -108,7 +108,7 @@ def show_problem(self, problem, file): class GithubFormater(Formater): - """The parsable formater.""" + """The github formater.""" name = 'github' def show_problems_for_all_files(self, all_problems): @@ -149,7 +149,7 @@ def show_problem(self, problem, file): class ColoredFormater(Formater): - """The parsable formater.""" + """The colored formater.""" name = 'colored' def show_problems_for_all_files(self, all_problems): @@ -187,7 +187,7 @@ def show_problem(self, problem, file): class StandardFormater(Formater): - """The parsable formater.""" + """The standard formater.""" name = 'standard' def show_problems_for_all_files(self, all_problems): @@ -222,7 +222,7 @@ def show_problem(self, problem, file): class JSONFormater(Formater): - """The parsable formater.""" + """The json formater.""" name = 'json' def show_problems_for_all_files(self, all_problems): From d24519c9415c9128a6518e2bfb0d0a1097ff6cf7 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Sat, 5 Feb 2022 11:24:48 +0100 Subject: [PATCH 11/32] feat: add codeclimate output --- yamllint/cli.py | 2 +- yamllint/format.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/yamllint/cli.py b/yamllint/cli.py index 471a2e0a..d9e364ea 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -59,7 +59,7 @@ def run(argv=None): help='custom configuration (as YAML source)') parser.add_argument('-f', '--format', choices=('parsable', 'standard', 'colored', 'github', - 'json', 'junitxml', 'auto'), + 'json', 'junitxml', 'codeclimate', 'auto'), default='auto', help='format for parsing output') parser.add_argument('-s', '--strict', action='store_true', diff --git a/yamllint/format.py b/yamllint/format.py index c2bbde15..565e89be 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -10,6 +10,13 @@ from yamllint.linter import PROBLEM_LEVELS +CODECLIMATE_SEVERITY = { + None: "info", + 'warning': "minor", + 'error': "major", +} + + def supports_color(): supported_platform = not (platform.system() == 'Windows' and not ('ANSICON' in os.environ or @@ -34,6 +41,12 @@ def escape_xml(text): return text +def severity_from_level(level): + if isinstance(level, int): + level = PROBLEM_LEVELS[level] + return CODECLIMATE_SEVERITY[level] + + class Formater(object): """Any formater.""" # the formater name @@ -286,6 +299,66 @@ def show_problem(self, problem, file): return {**problem.dict, "file": file} +class CodeclimateFormater(Formater): + """The codeclimate formater.""" + name = 'codeclimate' + + def show_problems_for_all_files(self, all_problems): + """Show all problems of all files.""" + lst = [] + for k, v in all_problems.items(): + lst += self.show_problems_for_file(v, k) + return json.dumps(lst, indent=4) + '\n' + + def show_problems_for_file(self, problems, file): + """Show all problems of a specific file.""" + return list(map(self.show_problem, problems, [file] * len(problems))) + + def show_problem(self, problem, file): + """Show all problems of a specific file. + + Using the codeclimate format. + https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#data-types + + + * `type` -- **Required**. Must always be "issue". + * `check_name` -- **Required**. A unique name representing the static analysis check that emitted this issue. + * `description` -- **Required**. A string explaining the issue that was detected. + * `content` -- **Optional**. A markdown snippet describing the issue, including deeper explanations and links to other resources. + * `categories` -- **Required**. At least one category indicating the nature of the issue being reported. + * `location` -- **Required**. A `Location` object representing the place in the source code where the issue was discovered. + * `trace` -- **Optional.** A `Trace` object representing other interesting source code locations related to this issue. + * `remediation_points` -- **Optional**. An integer indicating a rough estimate of how long it would take to resolve the reported issue. + * `severity` -- **Optional**. A `Severity` string (`info`, `minor`, `major`, `critical`, or `blocker`) describing the potential impact of the issue found. + * `fingerprint` -- **Optional**. A unique, deterministic identifier for the specific issue being reported to allow a user to exclude it from future analyses. + + For now the categories doc is empty, just put Style. + https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories + + I don't find a value of remdiation_points, just set it at 1k. + + I don't know how to calculate the fingerprint, maybe with a sha but it will be slow. + """ # noqa + return { + "type": "issue", + "check_name": problem.rule, + "description": problem.desc, + "content": problem.message, + "categories": ["Style"], + "location": { + "path": file, + "positions": { + "begin": { + "line": problem.line, + "column": problem.column + }, + } + }, + "remediation_points": 1_000, + "severity": severity_from_level(problem.level) + } + + def max_level(all_problems): """Return the max level of all problems.""" all_levels = [problem.level for problems in all_problems.values() for problem in problems] From 4baaf1200d5b1a2fce0a5346fcc235599db9a398 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Sat, 5 Feb 2022 11:26:50 +0100 Subject: [PATCH 12/32] feat: add message to the json format --- yamllint/linter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/yamllint/linter.py b/yamllint/linter.py index c35fc551..0fe2e0c0 100644 --- a/yamllint/linter.py +++ b/yamllint/linter.py @@ -71,6 +71,7 @@ def dict(self): "desc": self.desc, "rule": self.rule, "level": self.level, + "message": f"[{self.level}] {self.message}" } From e04f03a76c7ffaff9f18d57e111b96e4e6fde774 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Sat, 5 Feb 2022 11:40:17 +0100 Subject: [PATCH 13/32] feat: auto infer formats --- yamllint/cli.py | 5 ++--- yamllint/format.py | 5 +++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/yamllint/cli.py b/yamllint/cli.py index d9e364ea..7eb1cf1d 100644 --- a/yamllint/cli.py +++ b/yamllint/cli.py @@ -26,7 +26,7 @@ from yamllint import linter from yamllint.config import YamlLintConfig, YamlLintConfigError from yamllint.linter import PROBLEM_LEVELS -from yamllint.format import show_all_problems +from yamllint.format import show_all_problems, Formater def find_files_recursively(items, conf): @@ -58,8 +58,7 @@ def run(argv=None): action='store', help='custom configuration (as YAML source)') parser.add_argument('-f', '--format', - choices=('parsable', 'standard', 'colored', 'github', - 'json', 'junitxml', 'codeclimate', 'auto'), + choices=[*Formater.get_formaters_names(), 'auto'], default='auto', help='format for parsing output') parser.add_argument('-s', '--strict', action='store_true', diff --git a/yamllint/format.py b/yamllint/format.py index 565e89be..a1eb294e 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -52,6 +52,11 @@ class Formater(object): # the formater name name = '' + @classmethod + def get_formaters_names(cls): + """Return all formaters names.""" + return [f.name for f in cls.__subclasses__()] + @classmethod def get_formater(cls, name, no_warn): """Return a formater instance.""" From 40574518ba524f662216eed4ff53494857817785 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Tue, 8 Feb 2022 15:11:03 +0100 Subject: [PATCH 14/32] feat: changed json format according to @adrienverge comment --- yamllint/format.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/yamllint/format.py b/yamllint/format.py index a1eb294e..e3d9b080 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -255,8 +255,23 @@ def show_problems_for_file(self, problems, file): return list(map(self.show_problem, problems, [file] * len(problems))) def show_problem(self, problem, file): - """Show all problems of a specific file.""" - return {**problem.dict, "path": file} + """Show all problems of a specific file. + + The desired format is: + + >>> { + >>> "path": "dir/file.yaml", + >>> "line": 1337, + >>> "column": 42, + >>> "message": "duplication of key \"k\" in mapping", + >>> "rule": "key-duplicates", + >>> "level": "error" + >>> } + """ + dico = problem.dict + dico["message"] = dico.pop("desc") + dico["path"] = file + return dico class JunitFormater(Formater): From dab7a8089b7c3ddf9b45ddbe27f11299e9ff6609 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 12:08:12 +0100 Subject: [PATCH 15/32] fix: test_run_with_user_global_config_file patched --- .gitignore | 1 + tests/test_cli.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 723dc685..66c9dec7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __pycache__ /.eggs .venv venv +.coverage diff --git a/tests/test_cli.py b/tests/test_cli.py index 95e3fc74..e1486017 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -305,6 +305,9 @@ def test_run_with_user_global_config_file(self): config = os.path.join(dir, 'config') self.addCleanup(os.environ.update, HOME=os.environ['HOME']) + # remove other env vars to make sure we are using the HOME config file. + os.environ.pop('YAMLLINT_CONFIG_FILE', None) + os.environ.pop('XDG_CONFIG_HOME', None) os.environ['HOME'] = home with open(config, 'w') as f: From b1ce2f290bc2e095492f8cec4b4cb1298b391f01 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 12:17:38 +0100 Subject: [PATCH 16/32] test: escape_xml --- coverage.xml | 3919 ++++++++++++++++++++++++++++++++++++++++++ tests/test_format.py | 24 + yamllint/format.py | 2 +- 3 files changed, 3944 insertions(+), 1 deletion(-) create mode 100644 coverage.xml create mode 100644 tests/test_format.py diff --git a/coverage.xml b/coverage.xml new file mode 100644 index 00000000..8e4bf1c0 --- /dev/null +++ b/coverage.xml @@ -0,0 +1,3919 @@ + + + + + + /home/n42/git/yamllint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_format.py b/tests/test_format.py new file mode 100644 index 00000000..e9283973 --- /dev/null +++ b/tests/test_format.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +import unittest +import string + +from yamllint.format import escape_xml, severity_from_level + + +class TextToXMLTestCase(unittest.TestCase): + + specials = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + } + + def test_letters_chars(self): + txt = string.ascii_letters + self.assertEqual(escape_xml(txt), txt) + + def test_specials_chars(self): + for inp, out in self.specials.items(): + self.assertEqual(escape_xml(inp), out) diff --git a/yamllint/format.py b/yamllint/format.py index e3d9b080..b6f44d7f 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -37,7 +37,7 @@ def escape_xml(text): text = text.replace('<', '<') text = text.replace('>', '>') text = text.replace('"', '"') - text = text.replace('"', ''') + text = text.replace("'", ''') return text From 32de4472052b28f45b1197c07d43cfe117dc9069 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 12:20:42 +0100 Subject: [PATCH 17/32] test: severity_from_level --- coverage.xml | 17 +++++++++++------ tests/test_format.py | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/coverage.xml b/coverage.xml index 8e4bf1c0..3de357b4 100644 --- a/coverage.xml +++ b/coverage.xml @@ -1,5 +1,5 @@ - + @@ -606,6 +606,11 @@ + + + + + @@ -2449,7 +2454,7 @@ - + @@ -2670,7 +2675,7 @@ - + @@ -2694,9 +2699,9 @@ - - - + + + diff --git a/tests/test_format.py b/tests/test_format.py index e9283973..61897a00 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -22,3 +22,19 @@ def test_letters_chars(self): def test_specials_chars(self): for inp, out in self.specials.items(): self.assertEqual(escape_xml(inp), out) + + +class CodeClimateSeverityTestCase(unittest.TestCase): + + expected = { + None: "info", + 'warning': "minor", + 'error': "major", + 0: "info", + 1: "minor", + 2: "major", + } + + def test_specials_chars(self): + for inp, out in self.expected.items(): + self.assertEqual(severity_from_level(inp), out) From eb48e1d9082abbf0a1aea04ba4c216995ebd8e2c Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 12:41:50 +0100 Subject: [PATCH 18/32] test: Base class test --- coverage.xml | 57 +++++++++++++++++++++++++++++++------------- tests/test_format.py | 55 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 18 deletions(-) diff --git a/coverage.xml b/coverage.xml index 3de357b4..97a16266 100644 --- a/coverage.xml +++ b/coverage.xml @@ -1,12 +1,12 @@ - + /home/n42/git/yamllint - + @@ -598,19 +598,42 @@ - - - - - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2454,7 +2477,7 @@ - + @@ -2675,7 +2698,7 @@ - + @@ -2718,15 +2741,15 @@ - + - + - + - + diff --git a/tests/test_format.py b/tests/test_format.py index 61897a00..2152afb6 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -2,7 +2,21 @@ import unittest import string -from yamllint.format import escape_xml, severity_from_level +from yamllint.format import ( + escape_xml, + severity_from_level, + Formater, + ParsableFormater, + GithubFormater, + ColoredFormater, + StandardFormater, + JSONFormater, + JunitFormater, + CodeclimateFormater +) + + +INPUT = [] class TextToXMLTestCase(unittest.TestCase): @@ -38,3 +52,42 @@ class CodeClimateSeverityTestCase(unittest.TestCase): def test_specials_chars(self): for inp, out in self.expected.items(): self.assertEqual(severity_from_level(inp), out) + + +class FormaterTestCase(unittest.TestCase): + + sublcasses = { + "parsable": ParsableFormater, + "github": GithubFormater, + "colored": ColoredFormater, + "standard": StandardFormater, + "json": JSONFormater, + "junitxml": JunitFormater, + "codeclimate": CodeclimateFormater + } + + def test_get_formaters_names(self): + self.assertEqual( + set(Formater.get_formaters_names()), + set(self.sublcasses.keys()) + ) + + def test_get_formater(self): + for no_warn in [True, False]: + for name, cls in self.sublcasses.items(): + res = Formater.get_formater(name, no_warn) + self.assertTrue(isinstance(res, cls)) + self.assertEqual(res.no_warn, no_warn) + + def test_unknown_formater(self): + with self.assertRaises(ValueError): + Formater.get_formater("unknown", False) + + def test_abstract_class(self): + inst = Formater(False) + with self.assertRaises(NotImplementedError): + inst.show_problems_for_all_files([]) + with self.assertRaises(NotImplementedError): + inst.show_problems_for_file([], "a") + with self.assertRaises(NotImplementedError): + inst.show_problem(None, "a") From 1f6adf64f8673d6d72a7c69b167b2b9260289b97 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 12:49:51 +0100 Subject: [PATCH 19/32] test: Class to test all fmt --- coverage.xml | 6 +++++- tests/test_format.py | 17 ++++++++++++++--- yamllint/format.py | 6 +++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/coverage.xml b/coverage.xml index 97a16266..d58a31d6 100644 --- a/coverage.xml +++ b/coverage.xml @@ -1,5 +1,5 @@ - + @@ -634,6 +634,10 @@ + + + + diff --git a/tests/test_format.py b/tests/test_format.py index 2152afb6..e2ceb4b3 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -16,9 +16,6 @@ ) -INPUT = [] - - class TextToXMLTestCase(unittest.TestCase): specials = { @@ -91,3 +88,17 @@ def test_abstract_class(self): inst.show_problems_for_file([], "a") with self.assertRaises(NotImplementedError): inst.show_problem(None, "a") + + +class FormatersTestCase(unittest.TestCase): + + args = [ + (ParsableFormater(True), {"file1.yml": []}, ""), + ] + + def test_all_formaters(self): + for inst, inp, ret in self.args: + self.assertEqual( + inst.show_problems_for_all_files(inp), + ret + ) diff --git a/yamllint/format.py b/yamllint/format.py index b6f44d7f..95c77123 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -381,7 +381,11 @@ def show_problem(self, problem, file): def max_level(all_problems): """Return the max level of all problems.""" - all_levels = [problem.level for problems in all_problems.values() for problem in problems] + all_levels = [ + problem.level + for problems in all_problems.values() + for problem in problems + ] if all_levels: return max(map(lambda x: PROBLEM_LEVELS[x], all_levels)) return 0 From b42ac81594801ae493feb8d445ddc193442950cd Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 13:07:59 +0100 Subject: [PATCH 20/32] chore: rm coverage --- .gitignore | 1 + coverage.xml | 3951 -------------------------------------------------- 2 files changed, 1 insertion(+), 3951 deletions(-) delete mode 100644 coverage.xml diff --git a/.gitignore b/.gitignore index 66c9dec7..48aa0a00 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ __pycache__ .venv venv .coverage +coverage.xml diff --git a/coverage.xml b/coverage.xml deleted file mode 100644 index d58a31d6..00000000 --- a/coverage.xml +++ /dev/null @@ -1,3951 +0,0 @@ - - - - - - /home/n42/git/yamllint - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From d666308eb67ba6acc67a651c40c64c97ccd50111 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 13:10:13 +0100 Subject: [PATCH 21/32] tests: use ddt to parametrize tests --- CONTRIBUTING.rst | 1 + tests/test_format.py | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 312c6d88..c32551fb 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -14,6 +14,7 @@ Pull Request Process .. code:: bash pip install --user . + pip install ddt python -m unittest discover # all tests... python -m unittest tests/rules/test_commas.py # or just some tests (faster) diff --git a/tests/test_format.py b/tests/test_format.py index e2ceb4b3..640318f3 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import unittest import string +import ddt from yamllint.format import ( escape_xml, @@ -90,15 +91,32 @@ def test_abstract_class(self): inst.show_problem(None, "a") -class FormatersTestCase(unittest.TestCase): +NONE = {} +NO_ERROR = {"file1.yml": []} +ONE_ERROR = {} +ONE_WARNING = {} + - args = [ - (ParsableFormater(True), {"file1.yml": []}, ""), - ] +@ddt.ddt +class FormatersTestCase(unittest.TestCase): - def test_all_formaters(self): - for inst, inp, ret in self.args: - self.assertEqual( - inst.show_problems_for_all_files(inp), - ret - ) + @ddt.data( + (ParsableFormater(True), NONE, ""), + (GithubFormater(True), NONE, ""), + (ColoredFormater(True), NONE, ""), + (StandardFormater(True), NONE, ""), + (JSONFormater(True), NONE, "[]\n"), + (CodeclimateFormater(True), NONE, "[]\n"), + (ParsableFormater(True), NO_ERROR, ""), + (GithubFormater(True), NO_ERROR, ""), + (ColoredFormater(True), NO_ERROR, ""), + (StandardFormater(True), NO_ERROR, ""), + (JSONFormater(True), NO_ERROR, "[]\n"), + (CodeclimateFormater(True), NO_ERROR, "[]\n"), + ) + @ddt.unpack + def test_all_formaters(self, inst, inp, ret): + self.assertEqual( + inst.show_problems_for_all_files(inp), + ret + ) From 4ae5487e3ed035c917ab292720f8f370499cb8f6 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 13:16:51 +0100 Subject: [PATCH 22/32] feat: add the possibility to pass the level as argument --- yamllint/linter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yamllint/linter.py b/yamllint/linter.py index 0fe2e0c0..c4b8bf41 100644 --- a/yamllint/linter.py +++ b/yamllint/linter.py @@ -33,7 +33,7 @@ class LintProblem(object): """Represents a linting problem found by yamllint.""" - def __init__(self, line, column, desc='', rule=None): + def __init__(self, line, column, desc='', rule=None, level=None): #: Line on which the problem was found (starting at 1) self.line = line #: Column on which the problem was found (starting at 1) @@ -42,7 +42,7 @@ def __init__(self, line, column, desc='', rule=None): self.desc = desc #: Identifier of the rule that detected the problem self.rule = rule - self.level = None + self.level = level @property def message(self): From 418952cc5c3b827cdb67aa955b002f4b0728b268 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 13:18:13 +0100 Subject: [PATCH 23/32] refactor: match flake requirements --- yamllint/linter.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/yamllint/linter.py b/yamllint/linter.py index c4b8bf41..f3ede521 100644 --- a/yamllint/linter.py +++ b/yamllint/linter.py @@ -33,7 +33,14 @@ class LintProblem(object): """Represents a linting problem found by yamllint.""" - def __init__(self, line, column, desc='', rule=None, level=None): + def __init__( + self, + line, + column, + desc='', + rule=None, + level=None + ): #: Line on which the problem was found (starting at 1) self.line = line #: Column on which the problem was found (starting at 1) From 48b5e2b4041ad57f093bd06e7161a23454daf6f4 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 13:28:43 +0100 Subject: [PATCH 24/32] tests: tested formats with errors --- tests/test_format.py | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/tests/test_format.py b/tests/test_format.py index 640318f3..227f8654 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -3,6 +3,7 @@ import string import ddt +from yamllint.linter import LintProblem from yamllint.format import ( escape_xml, severity_from_level, @@ -93,8 +94,27 @@ def test_abstract_class(self): NONE = {} NO_ERROR = {"file1.yml": []} -ONE_ERROR = {} -ONE_WARNING = {} +ONE_NOTHING = {"file1.yml": [ + LintProblem(1, 1, desc="desc of None", rule="my-rule") +]} +ONE_ERROR = {"file1.yml": [ + LintProblem( + line=1, + column=2, + desc="desc of error", + rule="my-rule", + level="error" + ) +]} +ONE_WARNING = {"file1.yml": [ + LintProblem( + line=1, + column=2, + desc="desc of warn", + rule="my-rule", + level="warning" + ) +]} @ddt.ddt @@ -113,6 +133,24 @@ class FormatersTestCase(unittest.TestCase): (StandardFormater(True), NO_ERROR, ""), (JSONFormater(True), NO_ERROR, "[]\n"), (CodeclimateFormater(True), NO_ERROR, "[]\n"), + (ParsableFormater(True), ONE_NOTHING, ""), + (GithubFormater(True), ONE_NOTHING, ""), + (ColoredFormater(True), ONE_NOTHING, ""), + (StandardFormater(True), ONE_NOTHING, ""), + (JSONFormater(True), ONE_NOTHING, '[]\n'), + (CodeclimateFormater(True), ONE_NOTHING, '[]\n'), + (ParsableFormater(True), ONE_WARNING, ""), + (GithubFormater(True), ONE_WARNING, ""), + (ColoredFormater(True), ONE_WARNING, ""), + (StandardFormater(True), ONE_WARNING, ""), + (JSONFormater(True), ONE_WARNING, '[\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "warning",\n "message": "desc of warn",\n "path": "file1.yml"\n }\n]\n'), + (CodeclimateFormater(True), ONE_WARNING, '[\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of warn",\n "content": "desc of warn (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file1.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "minor"\n }\n]\n'), + (ParsableFormater(True), ONE_ERROR, 'file1.yml:1:2: [error] desc of error (my-rule)\n'), + (GithubFormater(True), ONE_ERROR, '::group::file1.yml\n::error file=file1.yml,line=1,col=2::1:2 [my-rule] desc of error\n::endgroup::\n\n'), + (ColoredFormater(True), ONE_ERROR, '\x1b[4mfile1.yml\x1b[0m\n \x1b[2m1:2\x1b[0m \x1b[31merror\x1b[0m desc of error \x1b[2m(my-rule)\x1b[0m\n\n'), + (StandardFormater(True), ONE_ERROR, 'file1.yml\n 1:2 error desc of error (my-rule)\n\n'), + (JSONFormater(True), ONE_ERROR, '[\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "error",\n "message": "desc of error",\n "path": "file1.yml"\n }\n]\n'), + (CodeclimateFormater(True), ONE_ERROR, '[\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of error",\n "content": "desc of error (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file1.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "major"\n }\n]\n'), ) @ddt.unpack def test_all_formaters(self, inst, inp, ret): From e018da186ebde67559464286fd33763e422e7f71 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 10 Feb 2022 13:35:39 +0100 Subject: [PATCH 25/32] feat: fixed some isues --- tests/test_format.py | 6 ++++-- yamllint/format.py | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/test_format.py b/tests/test_format.py index 227f8654..a1c4261f 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -143,8 +143,8 @@ class FormatersTestCase(unittest.TestCase): (GithubFormater(True), ONE_WARNING, ""), (ColoredFormater(True), ONE_WARNING, ""), (StandardFormater(True), ONE_WARNING, ""), - (JSONFormater(True), ONE_WARNING, '[\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "warning",\n "message": "desc of warn",\n "path": "file1.yml"\n }\n]\n'), - (CodeclimateFormater(True), ONE_WARNING, '[\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of warn",\n "content": "desc of warn (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file1.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "minor"\n }\n]\n'), + (JSONFormater(True), ONE_WARNING, '[]\n'), + (CodeclimateFormater(True), ONE_WARNING, '[]\n'), (ParsableFormater(True), ONE_ERROR, 'file1.yml:1:2: [error] desc of error (my-rule)\n'), (GithubFormater(True), ONE_ERROR, '::group::file1.yml\n::error file=file1.yml,line=1,col=2::1:2 [my-rule] desc of error\n::endgroup::\n\n'), (ColoredFormater(True), ONE_ERROR, '\x1b[4mfile1.yml\x1b[0m\n \x1b[2m1:2\x1b[0m \x1b[31merror\x1b[0m desc of error \x1b[2m(my-rule)\x1b[0m\n\n'), @@ -154,6 +154,8 @@ class FormatersTestCase(unittest.TestCase): ) @ddt.unpack def test_all_formaters(self, inst, inp, ret): + if inst.show_problems_for_all_files(inp) != ret: + print(f"\n{inst.__class__.__name__}\n" + inst.show_problems_for_all_files(inp)) self.assertEqual( inst.show_problems_for_all_files(inp), ret diff --git a/yamllint/format.py b/yamllint/format.py index 95c77123..850bfa12 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -252,7 +252,16 @@ def show_problems_for_all_files(self, all_problems): def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" - return list(map(self.show_problem, problems, [file] * len(problems))) + return list( + filter( + lambda x: x["level"] == "error" or not self.no_warn, + map( + self.show_problem, + problems, + [file] * len(problems) + ) + ) + ) def show_problem(self, problem, file): """Show all problems of a specific file. @@ -332,7 +341,16 @@ def show_problems_for_all_files(self, all_problems): def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" - return list(map(self.show_problem, problems, [file] * len(problems))) + return list( + filter( + lambda x: x["severity"] == "major" or not self.no_warn, + map( + self.show_problem, + problems, + [file] * len(problems) + ) + ) + ) def show_problem(self, problem, file): """Show all problems of a specific file. From 0354c335c4a012d3f29a6561f6e4a0cd650fafda Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Fri, 11 Feb 2022 08:45:57 +0100 Subject: [PATCH 26/32] ci: add ddt to the tests requirements and update the contributing file accordingly --- .github/workflows/ci.yaml | 2 +- CONTRIBUTING.rst | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 26cea6cb..d91fb00d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -45,7 +45,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Append GitHub Actions system path run: echo "$HOME/.local/bin" >> $GITHUB_PATH - - run: pip install coveralls + - run: pip install coveralls ddt - run: pip install . - run: coverage run --source=yamllint -m unittest discover - name: Coveralls diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index c32551fb..b32dd641 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -14,9 +14,12 @@ Pull Request Process .. code:: bash pip install --user . - pip install ddt - python -m unittest discover # all tests... - python -m unittest tests/rules/test_commas.py # or just some tests (faster) + pip install coveralls ddt + # all tests... + python -m coverage run --source=yamllint -m unittest discover + coverage report + # or just some tests (faster) + python -m unittest tests/rules/test_commas.py 3. If you add code that should be tested, add tests. From 3561a77070b1546425dbf4d849eb2cc61d6b1c7f Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Fri, 11 Feb 2022 08:55:18 +0100 Subject: [PATCH 27/32] tests: testing skipping warn with None level --- tests/test_format.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/test_format.py b/tests/test_format.py index a1c4261f..05e3e9d4 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -121,6 +121,7 @@ def test_abstract_class(self): class FormatersTestCase(unittest.TestCase): @ddt.data( + # No errors (ParsableFormater(True), NONE, ""), (GithubFormater(True), NONE, ""), (ColoredFormater(True), NONE, ""), @@ -139,12 +140,28 @@ class FormatersTestCase(unittest.TestCase): (StandardFormater(True), ONE_NOTHING, ""), (JSONFormater(True), ONE_NOTHING, '[]\n'), (CodeclimateFormater(True), ONE_NOTHING, '[]\n'), + # Errors with no level are ignored + (ParsableFormater(False), ONE_NOTHING, ""), + (GithubFormater(False), ONE_NOTHING, ""), + (ColoredFormater(False), ONE_NOTHING, ""), + (StandardFormater(False), ONE_NOTHING, ""), + (JSONFormater(False), ONE_NOTHING, '[]\n'), + (CodeclimateFormater(False), ONE_NOTHING, '[]\n'), + # 1 Skipped warning (ParsableFormater(True), ONE_WARNING, ""), (GithubFormater(True), ONE_WARNING, ""), (ColoredFormater(True), ONE_WARNING, ""), (StandardFormater(True), ONE_WARNING, ""), (JSONFormater(True), ONE_WARNING, '[]\n'), (CodeclimateFormater(True), ONE_WARNING, '[]\n'), + # 1 Unskipped warning + (ParsableFormater(False), ONE_WARNING, 'file1.yml:1:2: [warning] desc of warn (my-rule)\n'), + (GithubFormater(False), ONE_WARNING, '::group::file1.yml\n::warning file=file1.yml,line=1,col=2::1:2 [my-rule] desc of warn\n::endgroup::\n\n'), + (ColoredFormater(False), ONE_WARNING, '\x1b[4mfile1.yml\x1b[0m\n \x1b[2m1:2\x1b[0m \x1b[33mwarning\x1b[0m desc of warn \x1b[2m(my-rule)\x1b[0m\n\n'), + (StandardFormater(False), ONE_WARNING, 'file1.yml\n 1:2 warning desc of warn (my-rule)\n\n'), + (JSONFormater(False), ONE_WARNING, '[\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "warning",\n "message": "desc of warn",\n "path": "file1.yml"\n }\n]\n'), + (CodeclimateFormater(False), ONE_WARNING, '[\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of warn",\n "content": "desc of warn (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file1.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "minor"\n }\n]\n'), + # 1 Error (ParsableFormater(True), ONE_ERROR, 'file1.yml:1:2: [error] desc of error (my-rule)\n'), (GithubFormater(True), ONE_ERROR, '::group::file1.yml\n::error file=file1.yml,line=1,col=2::1:2 [my-rule] desc of error\n::endgroup::\n\n'), (ColoredFormater(True), ONE_ERROR, '\x1b[4mfile1.yml\x1b[0m\n \x1b[2m1:2\x1b[0m \x1b[31merror\x1b[0m desc of error \x1b[2m(my-rule)\x1b[0m\n\n'), @@ -155,7 +172,7 @@ class FormatersTestCase(unittest.TestCase): @ddt.unpack def test_all_formaters(self, inst, inp, ret): if inst.show_problems_for_all_files(inp) != ret: - print(f"\n{inst.__class__.__name__}\n" + inst.show_problems_for_all_files(inp)) + print(f"\n{inst.__class__.__name__}\n" + repr(inst.show_problems_for_all_files(inp))) self.assertEqual( inst.show_problems_for_all_files(inp), ret From 64e19e8a74d6004c594df9f1cf8a026bb9997af5 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Fri, 11 Feb 2022 09:07:44 +0100 Subject: [PATCH 28/32] fix: filter None to exclude them --- yamllint/format.py | 64 +++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/yamllint/format.py b/yamllint/format.py index 850bfa12..6875dd01 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -106,7 +106,10 @@ def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" string = '' for problem in problems: - string += self.show_problem(problem, file) + if problem.level is not None and ( + problem.level == "error" or not self.no_warn + ): + string += self.show_problem(problem, file) return string def show_problem(self, problem, file): @@ -140,7 +143,10 @@ def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" string = '::group::%s\n' % file for problem in problems: - string += self.show_problem(problem, file) + if problem.level is not None and ( + problem.level == "error" or not self.no_warn + ): + string += self.show_problem(problem, file) if string == '::group::%s\n' % file: return '' return string + '::endgroup::\n\n' @@ -181,7 +187,10 @@ def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" string = '\033[4m%s\033[0m\n' % file for problem in problems: - string += self.show_problem(problem, file) + if problem.level is not None and ( + problem.level == "error" or not self.no_warn + ): + string += self.show_problem(problem, file) if string == '\033[4m%s\033[0m\n' % file: return '' return string + '\n' @@ -219,7 +228,10 @@ def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" string = file + '\n' for problem in problems: - string += self.show_problem(problem, file) + if problem.level is not None and ( + problem.level == "error" or not self.no_warn + ): + string += self.show_problem(problem, file) if string == file + '\n': return '' return string + '\n' @@ -252,16 +264,13 @@ def show_problems_for_all_files(self, all_problems): def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" - return list( - filter( - lambda x: x["level"] == "error" or not self.no_warn, - map( - self.show_problem, - problems, - [file] * len(problems) - ) - ) - ) + lst = [] + for problem in problems: + if problem.level is not None and ( + problem.level == "error" or not self.no_warn + ): + lst.append(self.show_problem(problem, file)) + return lst def show_problem(self, problem, file): """Show all problems of a specific file. @@ -321,7 +330,13 @@ def show_problems_for_all_files(self, all_problems): def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" - return list(map(self.show_problem, problems, [file] * len(problems))) + lst = [] + for problem in problems: + if problem.level is not None and ( + problem.level == "error" or not self.no_warn + ): + lst.append(self.show_problem(problem, file)) + return lst def show_problem(self, problem, file): """Show all problems of a specific file.""" @@ -341,16 +356,13 @@ def show_problems_for_all_files(self, all_problems): def show_problems_for_file(self, problems, file): """Show all problems of a specific file.""" - return list( - filter( - lambda x: x["severity"] == "major" or not self.no_warn, - map( - self.show_problem, - problems, - [file] * len(problems) - ) - ) - ) + lst = [] + for problem in problems: + if problem.level is not None and ( + problem.level == "error" or not self.no_warn + ): + lst.append(self.show_problem(problem, file)) + return lst def show_problem(self, problem, file): """Show all problems of a specific file. @@ -405,7 +417,7 @@ def max_level(all_problems): for problem in problems ] if all_levels: - return max(map(lambda x: PROBLEM_LEVELS[x], all_levels)) + return max(map(lambda x: int(PROBLEM_LEVELS[x]), all_levels)) return 0 From f0000a18280fb03a9b82399c5edec2738599b870 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Fri, 11 Feb 2022 11:23:08 +0100 Subject: [PATCH 29/32] test: test with mixed errors / files --- tests/test_format.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/test_format.py b/tests/test_format.py index 05e3e9d4..ea7d6286 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +# flake8: noqa import unittest import string import ddt @@ -115,6 +116,16 @@ def test_abstract_class(self): level="warning" ) ]} +MIXED_ONE_FILE = {"file1.yml": [ + ONE_NOTHING["file1.yml"][0], + ONE_ERROR["file1.yml"][0], + ONE_WARNING["file1.yml"][0] +]} +MIXED_MULT_FILE = { + "file1.yml": ONE_NOTHING["file1.yml"], + "file2.yml": ONE_ERROR["file1.yml"], + "file3.yml": ONE_WARNING["file1.yml"] +} @ddt.ddt @@ -168,11 +179,23 @@ class FormatersTestCase(unittest.TestCase): (StandardFormater(True), ONE_ERROR, 'file1.yml\n 1:2 error desc of error (my-rule)\n\n'), (JSONFormater(True), ONE_ERROR, '[\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "error",\n "message": "desc of error",\n "path": "file1.yml"\n }\n]\n'), (CodeclimateFormater(True), ONE_ERROR, '[\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of error",\n "content": "desc of error (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file1.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "major"\n }\n]\n'), + # mixed warn / err on the same file + (ParsableFormater(False), MIXED_ONE_FILE, 'file1.yml:1:2: [error] desc of error (my-rule)\nfile1.yml:1:2: [warning] desc of warn (my-rule)\n'), + (GithubFormater(False), MIXED_ONE_FILE, '::group::file1.yml\n::error file=file1.yml,line=1,col=2::1:2 [my-rule] desc of error\n::warning file=file1.yml,line=1,col=2::1:2 [my-rule] desc of warn\n::endgroup::\n\n'), + (ColoredFormater(False), MIXED_ONE_FILE, '\x1b[4mfile1.yml\x1b[0m\n \x1b[2m1:2\x1b[0m \x1b[31merror\x1b[0m desc of error \x1b[2m(my-rule)\x1b[0m\n \x1b[2m1:2\x1b[0m \x1b[33mwarning\x1b[0m desc of warn \x1b[2m(my-rule)\x1b[0m\n\n'), + (StandardFormater(False), MIXED_ONE_FILE, 'file1.yml\n 1:2 error desc of error (my-rule)\n 1:2 warning desc of warn (my-rule)\n\n'), + (JSONFormater(False), MIXED_ONE_FILE, '[\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "error",\n "message": "desc of error",\n "path": "file1.yml"\n },\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "warning",\n "message": "desc of warn",\n "path": "file1.yml"\n }\n]\n'), + (CodeclimateFormater(False), MIXED_ONE_FILE, '[\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of error",\n "content": "desc of error (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file1.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "major"\n },\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of warn",\n "content": "desc of warn (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file1.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "minor"\n }\n]\n'), + # mixed warn / err on multiples files + (ParsableFormater(False), MIXED_MULT_FILE, 'file2.yml:1:2: [error] desc of error (my-rule)\nfile3.yml:1:2: [warning] desc of warn (my-rule)\n'), + (GithubFormater(False), MIXED_MULT_FILE, '::group::file2.yml\n::error file=file2.yml,line=1,col=2::1:2 [my-rule] desc of error\n::endgroup::\n\n::group::file3.yml\n::warning file=file3.yml,line=1,col=2::1:2 [my-rule] desc of warn\n::endgroup::\n\n'), + (ColoredFormater(False), MIXED_MULT_FILE, '\x1b[4mfile2.yml\x1b[0m\n \x1b[2m1:2\x1b[0m \x1b[31merror\x1b[0m desc of error \x1b[2m(my-rule)\x1b[0m\n\n\x1b[4mfile3.yml\x1b[0m\n \x1b[2m1:2\x1b[0m \x1b[33mwarning\x1b[0m desc of warn \x1b[2m(my-rule)\x1b[0m\n\n'), + (StandardFormater(False), MIXED_MULT_FILE, 'file2.yml\n 1:2 error desc of error (my-rule)\n\nfile3.yml\n 1:2 warning desc of warn (my-rule)\n\n'), + (JSONFormater(False), MIXED_MULT_FILE, '[\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "error",\n "message": "desc of error",\n "path": "file2.yml"\n },\n {\n "line": 1,\n "column": 2,\n "rule": "my-rule",\n "level": "warning",\n "message": "desc of warn",\n "path": "file3.yml"\n }\n]\n'), + (CodeclimateFormater(False), MIXED_MULT_FILE, '[\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of error",\n "content": "desc of error (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file2.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "major"\n },\n {\n "type": "issue",\n "check_name": "my-rule",\n "description": "desc of warn",\n "content": "desc of warn (my-rule)",\n "categories": [\n "Style"\n ],\n "location": {\n "path": "file3.yml",\n "positions": {\n "begin": {\n "line": 1,\n "column": 2\n }\n }\n },\n "remediation_points": 1000,\n "severity": "minor"\n }\n]\n'), ) @ddt.unpack def test_all_formaters(self, inst, inp, ret): - if inst.show_problems_for_all_files(inp) != ret: - print(f"\n{inst.__class__.__name__}\n" + repr(inst.show_problems_for_all_files(inp))) self.assertEqual( inst.show_problems_for_all_files(inp), ret From fba5b70d468ae1aba83a4aeeb5ee390357a18266 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Fri, 11 Feb 2022 11:40:33 +0100 Subject: [PATCH 30/32] tests: use ddt to others classes --- tests/test_format.py | 98 ++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 40 deletions(-) diff --git a/tests/test_format.py b/tests/test_format.py index ea7d6286..366103a8 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -19,65 +19,79 @@ ) +@ddt.ddt class TextToXMLTestCase(unittest.TestCase): - specials = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - } - def test_letters_chars(self): txt = string.ascii_letters self.assertEqual(escape_xml(txt), txt) - def test_specials_chars(self): - for inp, out in self.specials.items(): - self.assertEqual(escape_xml(inp), out) + @ddt.data( + ('&', '&'), + ('<', '<'), + ('>', '>'), + ('"', '"'), + ("'", '''), + ) + @ddt.unpack + def test_specials_chars(self, inp, out): + self.assertEqual(escape_xml(inp), out) +@ddt.ddt class CodeClimateSeverityTestCase(unittest.TestCase): - expected = { - None: "info", - 'warning': "minor", - 'error': "major", - 0: "info", - 1: "minor", - 2: "major", - } - - def test_specials_chars(self): - for inp, out in self.expected.items(): - self.assertEqual(severity_from_level(inp), out) + @ddt.data( + (None, "info"), + ('warning', "minor"), + ('error', "major"), + (0, "info"), + (1, "minor"), + (2, "major"), + ) + @ddt.unpack + def test_specials_chars(self, inp, out): + self.assertEqual(severity_from_level(inp), out) +@ddt.ddt class FormaterTestCase(unittest.TestCase): - sublcasses = { - "parsable": ParsableFormater, - "github": GithubFormater, - "colored": ColoredFormater, - "standard": StandardFormater, - "json": JSONFormater, - "junitxml": JunitFormater, - "codeclimate": CodeclimateFormater - } - def test_get_formaters_names(self): self.assertEqual( set(Formater.get_formaters_names()), - set(self.sublcasses.keys()) + { + "parsable", + "github", + "colored", + "standard", + "json", + "junitxml", + "codeclimate" + } ) - def test_get_formater(self): - for no_warn in [True, False]: - for name, cls in self.sublcasses.items(): - res = Formater.get_formater(name, no_warn) - self.assertTrue(isinstance(res, cls)) - self.assertEqual(res.no_warn, no_warn) + @ddt.data( + ("parsable", ParsableFormater, True), + ("github", GithubFormater, True), + ("colored", ColoredFormater, True), + ("standard", StandardFormater, True), + ("json", JSONFormater, True), + ("junitxml", JunitFormater, True), + ("codeclimate", CodeclimateFormater, True), + ("parsable", ParsableFormater, False), + ("github", GithubFormater, False), + ("colored", ColoredFormater, False), + ("standard", StandardFormater, False), + ("json", JSONFormater, False), + ("junitxml", JunitFormater, False), + ("codeclimate", CodeclimateFormater, False), + ) + @ddt.unpack + def test_get_formater(self, name, cls, no_warn): + res = Formater.get_formater(name, no_warn) + self.assertTrue(isinstance(res, cls)) + self.assertEqual(res.no_warn, no_warn) def test_unknown_formater(self): with self.assertRaises(ValueError): @@ -200,3 +214,7 @@ def test_all_formaters(self, inst, inp, ret): inst.show_problems_for_all_files(inp), ret ) + + +# TODO : test max level +# TODO : test junit From d7f59fe219560439fa138c671d6026c9124ea901 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Fri, 11 Feb 2022 11:44:47 +0100 Subject: [PATCH 31/32] test: escape real text --- tests/test_format.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_format.py b/tests/test_format.py index 366103a8..f1d6ac43 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -32,6 +32,8 @@ def test_letters_chars(self): ('>', '>'), ('"', '"'), ("'", '''), + ("too many blank lines (3 > 2)", 'too many blank lines (3 > 2)'), + ('line too long (100 > 80 characters)', 'line too long (100 > 80 characters)') ) @ddt.unpack def test_specials_chars(self, inp, out): From ad46f33b1d09ffada9bd259d5cc1fa8a612f1c12 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Fri, 11 Feb 2022 13:55:52 +0100 Subject: [PATCH 32/32] test: remaining tests done --- tests/test_format.py | 52 ++++++++++++++++++++++++++++++++++++++++++-- yamllint/format.py | 6 ++--- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/tests/test_format.py b/tests/test_format.py index f1d6ac43..1d08f9ae 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -8,6 +8,7 @@ from yamllint.format import ( escape_xml, severity_from_level, + max_level, Formater, ParsableFormater, GithubFormater, @@ -218,5 +219,52 @@ def test_all_formaters(self, inst, inp, ret): ) -# TODO : test max level -# TODO : test junit +@ddt.ddt +class MaxLevelTestCase(unittest.TestCase): + + @ddt.data( + (NONE, 0), + (NO_ERROR, 0), + (ONE_NOTHING, 0), + (ONE_ERROR, 2), + (ONE_WARNING, 1), + (MIXED_ONE_FILE, 2), + (MIXED_MULT_FILE, 2), + ) + @ddt.unpack + def test_all_formaters(self, inp, ret): + self.assertEqual(max_level(inp), ret) + +@ddt.ddt +class JunitTestCase(unittest.TestCase): + + @ddt.data( + (NONE, False, [], ['<\/error><\/testcase>', '<\/failure><\/testcase>'], 7), + (NO_ERROR, False, [], ['<\/error><\/testcase>', '<\/failure><\/testcase>'], 7), + (ONE_NOTHING, False, [], ['<\/error><\/testcase>', '<\/failure><\/testcase>'], 7), + (ONE_ERROR, False, ['<\/error><\/testcase>'], ['<\/failure><\/testcase>'], 7), + (ONE_WARNING, False, ['<\/failure><\/testcase>'], ['<\/error><\/testcase>'], 7), + (ONE_WARNING, True, [], ['<\/error><\/testcase>', '<\/failure><\/testcase>'], 7), + (MIXED_ONE_FILE, False, ['<\/error><\/testcase>', '<\/failure><\/testcase>'], [], 8), + (MIXED_MULT_FILE, False, ['<\/error><\/testcase>', '<\/failure><\/testcase>'], [], 8), + ) + @ddt.unpack + def test_all_formaters(self, inp, no_warn, contain, not_contain, length): + res = JunitFormater(no_warn).show_problems_for_all_files(inp) + self.assertTrue(res.startswith( + '\n' \ + '\n' \ + ' \n' \ + '\n')) + + self.assertEqual(len(res.split('\n')), length) diff --git a/yamllint/format.py b/yamllint/format.py index 6875dd01..287501d8 100644 --- a/yamllint/format.py +++ b/yamllint/format.py @@ -307,9 +307,7 @@ def show_problems_for_all_files(self, all_problems): lines = [] for item in lst: - if item['level'] is None: - continue - elif item['level'] == 'warning': + if item['level'] == 'warning': warnings += 1 to_append = '<\/failure><\/testcase>' # noqa elif item['level'] == 'error': @@ -323,7 +321,7 @@ def show_problems_for_all_files(self, all_problems): escape_xml(item['desc'])) ) - string += ' '*4 + '\n' % (errors, warnings, errors + warnings, datetime.datetime.now().isoformat(), platform.node()) # noqa + string += ' '*4 + '\n' % (errors, warnings, errors + warnings, datetime.datetime.now().isoformat(), platform.node()) # noqa string += '\n'.join(lines) + '\n' string += ' \n\n' return string